Хранение данных структуры в двоичный файл - PullRequest
1 голос
/ 02 июля 2010

Мне нужно хранить двоичный файл с 12-байтовым заголовком, состоящим из 4 полей.Это: sSamples (4-байтовое целое), sSampPeriod (4-байтовое целое), sSampSize (2-байтовое целое) и, наконец, sParmKind (2-байтовое целое).Я использую "struct" для моих переменных в желаемых полях.Теперь, когда я определил их отдельно, как я могу объединить их все для хранения '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 ]

2 голосов
/ 02 июля 2010

Как писал Коди Брошиус, вы можете упаковать весь заголовок сразу:

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 за указание на это.

EDIT : добавлен код numpy для выравнивания массива перед упаковкой и восстановления его после распаковки.

1 голос
/ 02 июля 2010

struct.pack возвращает строку, поэтому вы можете объединить поля просто путем объединения строк:

header = sSamples + sSampPeriod + sSampSize + sParmKind
assert len( header ) == 12
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...