Python结构错误

时间:2016-01-13 07:43:18

标签: python struct

我试图设计一个系统来对不同的二进制标志做出反应。

0 = Error
1 = Okay
2 = Logging
3 = Number

此数据的序列表示引用工作,标志和数字的唯一ID。除了数字标志外,一切正常。这就是我得到的......

>>> import struct
>>> data = (1234, 3, 12345678)
>>> bin = struct.pack('QHL', *data)
>>> print(bin)
b'\xd2\x04\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00Na\xbc\x00\x00\x00\x00\x00'
>>> result = struct.unpack_from('QH', bin, 0)
>>> print(result)
(1234, 3)
>>> offset = struct.calcsize('QH')
>>> result += struct.unpack_from('L', bin, offset)
>>> print(result)
(1234, 3, 7011541669862440960)

长度应该足够大以表示数字12345678,但为什么它被错误地解压缩?

修改

当我尝试单独打包时,看起来struct在标志和long之间添加了太多的空字节。

>>> import struct
>>> struct.pack('QH', 1234, 3)
b'\xd2\x04\x00\x00\x00\x00\x00\x00\x03\x00'
>>> struct.pack('L', 12345678)
b'Na\xbc\x00\x00\x00\x00\x00'

我可以通过在long之前添加填充来重现此错误。

>>> struct.unpack('L', struct.pack('L', 12345678))
(12345678,)
>>> struct.unpack('xL', struct.pack('xL', 12345678))
(12345678,)
>>> struct.pack('xL', 12345678)
b'\x00\x00\x00\x00\x00\x00\x00\x00Na\xbc\x00\x00\x00\x00\x00'

可能修复?

当我使用little-endian顺序时,问题似乎纠正了自己并使二进制字符串更短。由于这是一个SSL包装的TCP套接字,这是一个双赢,对吧?保持低带宽通常是好的,是吗?

>>> import struct
>>> data = (1234, 3, 12345678)
>>> bin = struct.pack('<QHL', *data)
>>> print(bin)
b'\xd2\x04\x00\x00\x00\x00\x00\x00\x03\x00Na\xbc\x00'
>>> result = struct.unpack_from('<QH', bin, 0)
>>> print(result)
(1234, 3)
>>> offset = struct.calcsize('<QH')
>>> result += struct.unpack_from('<L', bin, offset)
>>> print(result)
(1234, 3, 12345678)

为什么会这样?我很困惑。

3 个答案:

答案 0 :(得分:6)

您遇到了字节对齐问题。您需要知道,默认情况下,结构的各个部分不是彼此相邻放置,而是在内存中正确对齐。这使得它更有效,特别是对于其他应用程序,因为它们有更直接的方式从中访问单个字节而不必考虑重叠。

您可以使用struct.calcsize轻松查看此内容,以查看使用格式进行编码所需的空间:

>>> struct.calcsize('QHL')
16
>>> struct.calcsize('QH')
10

正如您所看到的,QHL需要16个字节,但QH需要10个。但是,我们停止的L只有4个字节宽。因此,有一些填充要确保L再次在“新块”上启动。这是因为任何类型都需要(使用填充)它从一个偏移量开始,该偏移量是其自身大小的倍数。对于QH,它看起来像这样:

QQ QQ | QQ QQ | HH

使用QHL后,您会收到以下信息:

QQ QQ | QQ QQ | HH 00 | LL LL

如您所见,添加了两个填充字节以确保L在新的四个块上启动。

您可以使用格式字符串开头的特殊字符修改对齐方式(以及字节顺序)。在您的情况下,您可以使用=QHL完全禁用对齐:

QQ QQ | QQ QQ | HH LL | LL
  

当我使用little-endian顺序时,问题似乎纠正了自己并使二进制字符串更短。由于这是一个SSL包装TCP套接字,这是一个双赢,对吧?保持低带宽通常是好的,是吗?

使用显式字节顺序also disables alignment是的,这就是效果的来源。但是,如果转向对齐是个好主意。如果你想在别的地方使用你的数据,那么坚持原生对齐是个好主意。

答案 1 :(得分:1)

您的案例中的正确输出:

>>> import struct
>>> data = (1234, 3, 12345678)
>>> bin = struct.pack('QHL', *data)
>>> print(bin)
b'\xd2\x04\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00Na\xbc\x00\x00\x00\x00\x00'
>>> result = struct.unpack_from('QH', bin, 0)
>>> print(result)
(1234, 3)
>>> result += struct.unpack_from('L', bin, 16)
>>> print(result)
(1234, 3, 12345678)

这是因为:

  

填充仅在连续的结构成员之间自动添加。

此外,您的修复工作的原因是:

  

使用非原生大小和对齐时,不添加填充,例如使用'&lt;','&gt;','='和'!'。

答案 2 :(得分:1)

这是一个字节对齐问题。在您的情况下,以下替换将为您提供正确的输出。

result += struct.unpack_from('L', bin, offset+2)