将'struct'数据存储为二进制文件

1 投票
2 回答
3280 浏览
提问于 2025-04-16 00:43

我需要存储一个二进制文件,这个文件的开头有一个12字节的头部,这个头部由4个字段组成。具体来说,这4个字段是:sSamples(4字节的整数)、sSampPeriod(4字节的整数)、sSampSize(2字节的整数),最后是sParmKind(2字节的整数)。

我正在使用'结构体'来定义这些变量,以便它们符合所需的字段。现在我已经分别定义了这些字段,接下来我该如何把它们合并在一起,以存储这个'12字节的头部'呢?

sSamples        = struct.pack('i', nSamples) # 4-bytes integer
sSampPeriod     = struct.pack('i', nSampPeriod) # 4-bytes integer
sSampSize       = struct.pack('H', nSampSize) # 2-bytes integer / unsigned short
sParmKind       = struct.pack('H', 9) # 2-bytes integer / unsigned short

另外,我还有一个名为npVect的浮点数组,维度为D(numpy.ndarray - float32)。我该如何在同一个二进制文件中存储这个向量,但要放在头部之后呢?

2 个回答

1

struct.pack这个函数会返回一个字符串,所以你可以通过简单地把字符串拼接在一起来组合这些字段:

header = sSamples + sSampPeriod + sSampSize + sParmKind
assert len( header ) == 12
2

正如Cody Brocious所说,你可以一次性把整个头部打包:

header = struct.pack('<iiHH', nSamples, nSampPeriod, nSampSize, nParmKind)

他还提到了字节序,这个很重要,如果你想把数据打包成可以在不同架构的机器上可靠解包的格式。格式字符串开头的<表示“使用小端字节序来打包这些数据”。

至于数组,你需要先打包它的长度,这样在再次读取时才能知道要解包多少个值。可以一次性完成所有操作:

flattened = npVect.ravel()  # get a 1-D array of numbers
arrSize = len(flattened)
# pack header, count of numbers, and numbers, all in one call
packed = struct.pack('<iiHHi%df' % arrSize,
    nSamples, nSampPeriod, nSampSize, nParmKind, arrSize, *flattened)

根据你的数组可能有多大,你可能会得到一个非常大的字符串,这个字符串代表了整个二进制文件的内容。你可能需要考虑一些不需要将整个文件放在内存中的替代方案,而不是使用struct

解包:

fmt = '<iiHHi'
nSamples, nSampPeriod, nSampSize, nParmKind, arrSize = struct.unpack(fmt, packed)
# Use unpack_from to start reading after the packed header and count
flattened = struct.unpack_from('<%df' % arrSize, packed, struct.calcsize(fmt))
npVect = np.ndarray(flattened, dtype='float32').reshape(# your dimensions go here
    )

编辑:哎呀,数组的格式并没有那么简单 :) 不过大体思路是对的:用你喜欢的任何方法把数组扁平化成一个数字列表,先打包值的数量,然后再打包每个值。在另一边,读取这个数组时作为一个扁平的列表,然后根据需要施加任何结构。

编辑:把格式字符串改成使用重复说明符,而不是字符串乘法。感谢John Machin的提醒。

编辑:添加了numpy代码,用于在打包前扁平化数组,并在解包后重建它。

撰写回答