根据先前的元素解析项目
Parse an item depending on previous elements
为了提供一些上下文,我正在解析一个 DICOM 文件,并且在使用传输语法条目来确定是使用隐式还是显式解析时遇到困难。但是让我定义一个简化的语法,所以不需要 dicom 知识。
我们有一个条目序列,每个条目都有一个 group number
和一个 data
部分。组号始终表示为 u2
,但数据可以是不同类型,比如 u2
或 u4
。条目的顺序可以是任意的,除了所有带有 group number == 2
的条目必须在顶部。所有带有 group number == 2
的条目都具有 u2
的数据类型,但后续数据部分可能不同。
困难的部分来了:所有带有 group number != 2
的项目都具有数据类型 u4
当且仅当之前存在与此完全相同的条目时:
(group, data) == (0x0002, 0x0101)
例如在python中,我会这样解析它:
def read_entries(stream):
is_u4 = False
while not stream.eos():
group = stream.read_u2()
if group != 2 and is_u4:
data = stream.read_u4()
else:
data = stream.read_u2()
if group == 2 and data == 0x0101:
is_u4 = True
yield (group, data)
有没有办法使用 kaitai-struct 实现这个?
简答
目前无法将 Python 代码准确转录为 KS 本身,只能通过 plugging in the code 用命令式语言编写。
但是,由于您提供了额外的信息,我们提供了一种替代方法,请参阅下面的解决方案。
更长的答案
Kaitai Struct 强调无状态解析,因此我们解析的所有内容实际上都是不可变的(只读),即在解析过程中存在可以更改其值的变量。因此在解析周期之间传播 is_u4
是一件非常重要的事情。例如,MIDI running status 我们有类似的问题。
人们建议的一些解决方案有时是递归类型定义 + 使用 _parent
语法传播实例(参见 issue #70),但是:
- 目前由于缺少递归定义的实例值的类型定义 + 缺少初始值播种而受到阻碍
- 这会生成一个值的链接列表,而不是一个数组(也就是说,大多数人都不喜欢这样)
- 这会对调用堆栈造成严重影响(即非常长的结构很可能会因堆栈耗尽而失败)
考虑到您提供的额外信息,替代方法可能是可行的。实际上,整个元素流可以分为 3 组:
- (0x0002, 0x0101) 之前的元素 - 它们都使用 u2-u2 格式
- (0x0002, 0x0101) 元素本身 - 请注意,它在第一次遇到时也始终是 u2-u2
- (0x0002, 0x0101) 之后的元素 - 它们都使用 u2-u4 格式;即使之后遇到额外的(0x0002, 0x0101),按照你提供的算法,应该读作u2-u4,所以没问题。
因此有可能逃脱:
types:
elements:
seq:
- id: elements_2
type: element_2
repeat-until: _.is_u4
- id: elements_4
type: element_4
repeat: eos
element_2:
seq:
- id: group
type: u2
- id: data
type: u2
instances:
is_u4:
value: group == 2 and data == 0x0101
element_4:
seq:
- id: group
type: u2
- id: data
type: u4
请告诉我我是否做对了,这对你的项目有用吗?
为了提供一些上下文,我正在解析一个 DICOM 文件,并且在使用传输语法条目来确定是使用隐式还是显式解析时遇到困难。但是让我定义一个简化的语法,所以不需要 dicom 知识。
我们有一个条目序列,每个条目都有一个 group number
和一个 data
部分。组号始终表示为 u2
,但数据可以是不同类型,比如 u2
或 u4
。条目的顺序可以是任意的,除了所有带有 group number == 2
的条目必须在顶部。所有带有 group number == 2
的条目都具有 u2
的数据类型,但后续数据部分可能不同。
困难的部分来了:所有带有 group number != 2
的项目都具有数据类型 u4
当且仅当之前存在与此完全相同的条目时:
(group, data) == (0x0002, 0x0101)
例如在python中,我会这样解析它:
def read_entries(stream):
is_u4 = False
while not stream.eos():
group = stream.read_u2()
if group != 2 and is_u4:
data = stream.read_u4()
else:
data = stream.read_u2()
if group == 2 and data == 0x0101:
is_u4 = True
yield (group, data)
有没有办法使用 kaitai-struct 实现这个?
简答
目前无法将 Python 代码准确转录为 KS 本身,只能通过 plugging in the code 用命令式语言编写。
但是,由于您提供了额外的信息,我们提供了一种替代方法,请参阅下面的解决方案。
更长的答案
Kaitai Struct 强调无状态解析,因此我们解析的所有内容实际上都是不可变的(只读),即在解析过程中存在可以更改其值的变量。因此在解析周期之间传播 is_u4
是一件非常重要的事情。例如,MIDI running status 我们有类似的问题。
人们建议的一些解决方案有时是递归类型定义 + 使用 _parent
语法传播实例(参见 issue #70),但是:
- 目前由于缺少递归定义的实例值的类型定义 + 缺少初始值播种而受到阻碍
- 这会生成一个值的链接列表,而不是一个数组(也就是说,大多数人都不喜欢这样)
- 这会对调用堆栈造成严重影响(即非常长的结构很可能会因堆栈耗尽而失败)
考虑到您提供的额外信息,替代方法可能是可行的。实际上,整个元素流可以分为 3 组:
- (0x0002, 0x0101) 之前的元素 - 它们都使用 u2-u2 格式
- (0x0002, 0x0101) 元素本身 - 请注意,它在第一次遇到时也始终是 u2-u2
- (0x0002, 0x0101) 之后的元素 - 它们都使用 u2-u4 格式;即使之后遇到额外的(0x0002, 0x0101),按照你提供的算法,应该读作u2-u4,所以没问题。
因此有可能逃脱:
types:
elements:
seq:
- id: elements_2
type: element_2
repeat-until: _.is_u4
- id: elements_4
type: element_4
repeat: eos
element_2:
seq:
- id: group
type: u2
- id: data
type: u2
instances:
is_u4:
value: group == 2 and data == 0x0101
element_4:
seq:
- id: group
type: u2
- id: data
type: u4
请告诉我我是否做对了,这对你的项目有用吗?