我试图设计一个系统来对不同的二进制标志做出反应。
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)
为什么会这样?我很困惑。
答案 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)