Python构造 - 消耗可选字段的数据

时间:2012-02-29 06:41:39

标签: python construct

使用Python construct库,我解析的数据有一个字段,只有在设置了标志时才有意义。

但是,数据字段始终存在。

因此,我想在任何情况下使用数据,但只根据标记的值设置字段值。

例如,如果结构(错误地)定义为:

struct = Struct("struct",
    Flag("flag"),
    UBInt8("optional_data"),
    UBInt8("mandatory")
)

对于数据:

>>> struct.parse("010203".decode("hex"))

结果应为:

Container({'flag': True, 'mandatory': 3, 'optional_data': 2})

对于数据:

>>> struct.parse("000203".decode("hex"))

期望的结果是:

Container({'flag': False, 'mandatory': 3, 'optional_data': None})

我尝试了以下内容:

struct = Struct("struct",
    Flag("flag"),
    IfThenElse("optional_data", lambda ctx: ctx.flag,
        UBInt8("dummy"),
        Padding(1)
    ),
    UBInt8("mandatory")
)

但是,Padding()将原始数据放在字段中,如下所示:

>>> struct.parse("000203".decode("hex"))
Container({'flag': False, 'mandatory': 3, 'optional_data': '\x02'})

谢谢

2 个答案:

答案 0 :(得分:1)

我不确定我是否正确理解你的问题。如果您的问题只是填充不被解析为int,那么您不需要IFThenElse。您可以在代码中检查已解析的容器的标志,并选择忽略optional_data字段。

struct = Struct("struct",
    Flag("flag"),
    UBInt8("optional_data"),
    UBInt8("mandatory")
)

如果您的问题是您希望仅在设置标志时使用名称可选数据但在未设置标志时使用名称dummy,则需要定义两个Ifs

struct = Struct("struct",
    Flag("flag"),
    If("optional_data", lambda ctx: ctx.flag,
        UBInt8("useful_byte"),
    ),
    If("dummy", lambda ctx: !ctx.flag,
        UBInt8("ignore_byte"),
    ),
    UBInt8("mandatory")
)

答案 1 :(得分:0)

也许您可以使用类似于LengthValueAdapter的适配器来实现序列

class LengthValueAdapter(Adapter):
"""
Adapter for length-value pairs. It extracts only the value from the 
pair, and calculates the length based on the value.
See PrefixedArray and PascalString.

Parameters:
* subcon - the subcon returning a length-value pair
"""
__slots__ = []
def _encode(self, obj, context):
    return (len(obj), obj)
def _decode(self, obj, context):
    return obj[1]

class OptionalDataAdapter(Adapter):
__slots__ = []
def _decode(self, obj, context):
  if context.flag:
    return obj
  else
    return None

所以

struct = Struct("struct",
    Flag("flag"),
    OptionalDataAdapter(UBInt8("optional_data")),
    UBInt8("mandatory")
)