Python struct calsize 与实际不同

Python struct calsize different from actual

我正在尝试使用 python struct 从二进制文件中读取一个短的和一个长的。

但是

print(struct.calcsize("hl")) # o/p 16

错了,应该是短的2个字节,长的8个字节。我不确定我是否以错误的方式使用 struct 模块。

当我打印每个值时,它是

print(struct.calcsize("h")) # o/p 2
print(struct.calcsize("l")) # o/p 8

有没有办法强制 python 保持 datatypes 的精度?

doc 是这么说的:

By default, the result of packing a given C struct includes pad bytes in order to maintain proper alignment for the C types involved; similarly, alignment is taken into account when unpacking. This behavior is chosen so that the bytes of a packed struct correspond exactly to the layout in memory of the corresponding C struct. To handle platform-independent data formats or omit implicit pad bytes, use standard size and alignment instead of native size and alignment

将它从标准更改为本机非常简单:您只需在格式字符前附加前缀 =

print(struct.calcsize("=hl"))

编辑

由于从原生模式到标准模式,一些默认尺寸发生了变化,您有两个选择:

  • 保持原生模式,但切换格式字符,这样:struct.calcsize("lh")。在 C 中,甚至结构中变量的顺序也很重要。这里的padding是8个字节,也就是说每个变量都必须以8个字节的倍数引用。

  • 使用标准模式的格式字符,所以:struct.calcsize("=hq")

默认结构对齐规则,16 是正确答案。每个字段都对齐以匹配其大小,因此您最终得到两个字节的 short,然后是六个字节的填充(以到达与八字节的倍数对齐的下一个地址),然后是八个字节的 long.

您可以使用 byte order prefix(它们中的任何一个禁用填充),但它们 禁用机器本机大小(因此 struct.calcsize("=l") 将是固定的在所有系统上都是 4 字节,并且 struct.calcsize("=hl") 在所有系统上都是 6 字节,而不是 10,即使在具有 8 字节 longs 的系统上也是如此。

如果您想使用具有非默认填充规则的机器本机类型计算任意结构的结构大小,您需要转到 ctypes 模块,定义您的 ctypes.Structure subclass with the desired _pack_ setting, then use ctypes.sizeof 以检查大小,例如:

from ctypes import Structure, c_long, c_short, sizeof

class HL(Structure):
    _pack_ = 1  # Disables padding for field alignment
    # Defines (unnamed) fields, a short followed by long
    _fields_ = [("", c_short),
               ("", c_long)]

print(sizeof(HL))

根据需要输出 10

如果需要,可以将其分解为实用函数(这是一个简化示例,无法处理所有 struct 格式代码,但您可以根据需要进行扩展):

from ctypes import *

FMT_TO_TYPE = dict(zip("cb?hHiIlLqQnNfd",
                       (c_char, c_byte, c_bool, c_short, c_ushort, c_int, c_uint,
                        c_long, c_ulong, c_longlong, c_ulonglong, 
                        c_ssize_t, c_size_t, c_float, c_double)))

def calcsize(fmt, pack=None):
    '''Compute size of a format string with arbitrary padding (defaults to native)'''
    class _(Structure):
        if pack is not None:
            _pack_ = pack
        _fields_ = [("", FMT_TO_TYPE[c]) for c in fmt]
    return sizeof(_)

定义后,您可以像这样计算填充或未填充的大小:

>>> calcsize("hl")     # Defaults to native "natural" alignment padding
16
>>> calcsize("hl", 1)  # pack=1 means no alignment padding between members
10