Python结构错误

bkv*_*eal 4 python struct

我正在尝试设计一个系统来对不同的二进制标志做出反应.

0 = Error
1 = Okay
2 = Logging
3 = Number
Run Code Online (Sandbox Code Playgroud)

此数据的序列表示引用工作,标志和数字的唯一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)
Run Code Online (Sandbox Code Playgroud)

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

编辑:

当我尝试单独打包时,看起来struct在flag和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'
Run Code Online (Sandbox Code Playgroud)

我可以通过在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'
Run Code Online (Sandbox Code Playgroud)

潜在的修复?

当我使用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)
Run Code Online (Sandbox Code Playgroud)

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

pok*_*oke 7

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

通过struct.calcsize使用以下格式查看编码所需的空间,您可以轻松地看到这一点:

>>> struct.calcsize('QHL')
16
>>> struct.calcsize('QH')
10
Run Code Online (Sandbox Code Playgroud)

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

QQ QQ | QQ QQ | HH
Run Code Online (Sandbox Code Playgroud)

使用后QHL,您将获得以下信息:

QQ QQ | QQ QQ | HH 00 | LL LL
Run Code Online (Sandbox Code Playgroud)

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

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

QQ QQ | QQ QQ | HH LL | LL
Run Code Online (Sandbox Code Playgroud)

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

使用显式字节顺序也会禁用对齐 yes,因此效果来自.但是,如果转向对齐是个好主意.如果你想在其他地方使用消费你的数据,那么坚持原生对齐是个好主意.