在 struct.unpack 格式字符串中间切换字节顺序

Posted

技术标签:

【中文标题】在 struct.unpack 格式字符串中间切换字节顺序【英文标题】:Switching endianness in the middle of a struct.unpack format string 【发布时间】:2018-07-28 10:56:27 【问题描述】:

我有一堆二进制数据(恰好是视频游戏保存文件的内容),其中一部分数据同时包含小端 大端整数值。天真地,没有阅读太多文档,我试图以这种方式解压它......

struct.unpack(
    '3sB<H<H<H<H4s<I<I32s>IbBbBbBbB12s20sBB4s',
    string_data
)

...当然,我收到了这条神秘的错误消息:

struct.error: bad char in struct format

问题在于struct.unpack 格式字符串不期望单个字段 被标记为字节序。此处实际正确的格式字符串类似于

struct.unpack(
    '<3sBHHHH4sII32sIbBbBbBbB12s20sBB4s',
    string_data
)

除了这将翻转第三个I 字段的字节序(当我真的想将其解析为大字节序时,将其解析为小字节序)。

我的问题有简单和/或“Pythonic”的解决方案吗?我已经想到了三种可能的解决方案,但没有一个是特别优雅的。如果没有更好的想法,我可能会选择 3:

    我可以提取一个子字符串并单独解析它:

    (my.f1, my.f2, ...) = struct.unpack('<3sBHHHH4sII32sIbBbBbBbB12s20sBB4s', string_data)
    my.f11 = struct.unpack('>I', string_data[56:60])
    

    事后我可以flip the bits in the field:

    (my.f1, my.f2, ...) = struct.unpack('<3sBHHHH4sII32sIbBbBbBbB12s20sBB4s', string_data)
    my.f11 = swap32(my.f11)
    

    我可以更改我的下游代码以期望该字段以不同的方式表示 - 它实际上是一个位掩码,而不是算术整数,因此它不会难以翻转我使用的所有位掩码;但这些位掩码的大端版本比小端版本更具有助记性。

【问题讨论】:

我认为这里在概念上有些问题。不应该有字节序混合。该修复会影响您需要解压缩的字符串的来源。关于下游代码选项。这处理一个 int (已经转换),它自动使用它运行的机器的字节序。 @CristiFati:我解压的字符串来自游戏存档文件格式。我不控制它的编码细节;我无法改变它们。我所能做的就是尝试处理给我的编码,给我的编码确实以这种精确的方式混合字节序。 作为更广泛的示例,ISO 9660 文件系统在某些地方将整数编码为小端和大端。通常是这样,您可以选择更容易在您的架构上使用的格式,但如果检查数据的完整性,解码两者并检查它们是否相等可能会很有用。 【参考方案1】:

聚会有点晚了,但我也遇到了同样的问题。我用一个自定义的 numpy dtype 解决了这个问题,它允许混合具有不同字节序的元素(参见https://numpy.org/doc/stable/reference/generated/numpy.dtype.html):

t=np.dtype('>u4,<u4') # Compound type with two 4-byte unsigned int with different byte order
a=np.zeros(shape=1, dtype=t) # Create an array of length one with above type
a[0][0]=1 # Assign first uint
a[0][1]=1 # Assign second uint
bytes=a.tobytes() # bytes should be b'\x01\x00\x00\x00\x00\x00\x00\x01'
b=np.frombuffer(buf, dtype=t) # should yield array[(1,1)]
c=np.frombuffer(buf, dtype=np.uint32) # yields array([       1, 16777216]

【讨论】:

以上是关于在 struct.unpack 格式字符串中间切换字节顺序的主要内容,如果未能解决你的问题,请参考以下文章

对于具有数据的二进制数据文件,在 struct unpack 命令中选择啥格式:b'@T\x89\x00\x00\x00\x00\x80$\xcds?

struct.unpack() struct.error: unpack 需要 124 字节的缓冲区

使用 struct.unpack_from() 解包混合二进制数据

如何使用 python struct.unpack

了解 struct.unpack python

struct模块拆分字符串为指定字符数的字符串或元组