Python struct.unpack (ing), когда есть несколько порядков байтов? - PullRequest
0 голосов
/ 22 февраля 2019

У меня есть функция, которая читает двоичный файл, а затем распаковывает содержимое файла с помощью struct.unpack ().Моя функция работает просто отлично.Это быстрее, если / когда я распакую весь файл, используя длинную строку формата.Проблема в том, что иногда выравнивание байтов меняется, поэтому моя строка формата (которая недопустима) будет выглядеть как <10sHHb> llh (это всего лишь пример (обычно они длиннее)).Есть ли какой-нибудь ультра гладкий / питонный способ справиться с этой ситуацией?

1 Ответ

0 голосов
/ 22 февраля 2019

Ничего сверхсложного, но если скорость имеет значение, функции верхнего уровня модуля 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'):]

Вы бы сделали:

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:]

Нет, это не очень хорошо выглядит, и выигрыш в скорости вряд ли вас унесет.Но у вас есть странные данные, так что это наименее хрупкое решение.

Если вы хотите излишне умные / хрупкие решения, вы можете сделать это с помощью ctypes custom Structure s., вложив BigEndianStructure (s) внутрь 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

даст вам такую ​​структуру, которую вы могли бы сделать:

mystruct = MainStruct()
memoryview(mystruct).cast('B')[:] = bytes(range(25))

, и тогда вы получите результаты в ожидаемом порядке, например:

>>> 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'

Будучи умным в некотором смысле, он, вероятно, будет работать медленнее (поиск по атрибутам ctypes странно медленен в моем опыте), и в отличие от struct функций модуля, вы не можете просто распаковать в top-именованные переменные в одной строке, доступ к атрибутам полностью.

...