Python struct.unpack(ing) 当有多个字节顺序时?

Jes*_*bin 5 python performance binaryfiles python-3.x iterable-unpacking

我有一个函数可以读取二进制文件,然后使用 struct.unpack() 解压缩文件的内容。我的功能工作得很好。如果/当我使用长“格式”字符串解压缩整个文件时,速度会更快。问题是有时字节对齐会发生变化,所以我的格式字符串(无效)看起来像 '<10sHHb>llh' (这只是一个例子(它们通常更长))。有没有处理这种情况的超灵巧/pythonic 方法?

Sha*_*ger 3

没什么超级流畅的,但如果速度很重要的话,struct模块顶级函数是包装器,必须重复重新检查缓存以查找与struct.Struct格式字符串相对应的实际实例;虽然您必须创建单独的格式字符串,但您可以通过避免重复的缓存检查来解决部分速度问题。

而不是这样做:

buffer = memoryview(somedata)
allresults = []
while buffer:
    allresults += struct.unpack_from('<10sHHb', buffer)
    buffer = buffer[struct.calcsize('<10sHHb'):]
    allresults += struct.unpack_from('>llh', buffer)
    buffer = buffer[struct.calcsize('>llh'):]
Run Code Online (Sandbox Code Playgroud)

你会这样做:

buffer = memoryview(somedata)
structa = struct.Struct('<10sHHb')
structb = struct.Struct('>llh')
allresults = []
while buffer:
    allresults += structa.unpack_from(buffer)
    buffer = buffer[structa.size:]
    allresults += structb.unpack_from(buffer)
    buffer = buffer[structb.size:]
Run Code Online (Sandbox Code Playgroud)

不,它的外观并没有好多少,而且速度的提升也不可能让你大吃一惊。但你有奇怪的数据,所以这是最不脆弱的解决方案。

如果您想要不必要的聪明/脆弱的解决方案,您可以使用ctypes自定义Structures、嵌套BigEndianStructure在 a 中来实现此目的LittleEndianStructure,反之亦然。对于您的示例格式:

from ctypes import *

class BEStruct(BigEndianStructure):
    _fields_ = [('x', 2 * c_long), ('y', c_short)]
    _pack_ = True

class MainStruct(LittleEndianStructure):
    _fields_ = [('a', 10 * c_char), ('b', 2 * c_ushort), ('c', c_byte), ('big', BEStruct)]
    _pack_ = True
Run Code Online (Sandbox Code Playgroud)

会给你一个结构,你可以这样做:

mystruct = MainStruct()
memoryview(mystruct).cast('B')[:] = bytes(range(25))
Run Code Online (Sandbox Code Playgroud)

然后你会按照预期的顺序得到结果,例如:

>>> hex(mystruct.b[0])  # Little endian as expected in main struct
'0xb0a'
>>> hex(mystruct.big.x[0]) # Big endian from inner big endian structure
'0xf101112'
Run Code Online (Sandbox Code Playgroud)

虽然在某种程度上很聪明,但它可能会运行得更慢(ctypes根据我的经验,属性查找非常慢),并且与struct模块函数不同,您不能只在一行中解压到顶级命名变量中,它的属性访问所有方式。