Struct.error: unpack требует строкового аргумента длиной 4

Python говорит, что мне нужен 4 байта для кода формата "BH":

struct.error: unpack requires a string argument of length 4

Вот код, я помещаю 3 байта, как мне кажется, нужно:

major, minor = struct.unpack("BH", self.fp.read(3))

"B" Unsigned char (1 байт) + "H" Беззнаковый короткий (2 байта) = 3 байта (!?)

struct.calcsize( "BH" ) говорит 4 байта.

EDIT: файл составляет ~ 800 МБ, и это находится в первых байтах файла, поэтому я уверен, что данные будут оставлены для чтения.

Ответы

Ответ 1

Структурный модуль имитирует структуры C. Процессу требуется больше циклов процессора для чтения 16-разрядного слова на нечетном адресе или 32-битном dword по адресу, не делящемуся на 4, поэтому структуры добавляют "байты на панели", чтобы члены структуры падали на естественные границы. Рассмотрим:

struct {                   11
    char a;      012345678901
    short b;     ------------
    char c;      axbbcxxxdddd
    int d;
};

Эта структура будет занимать 12 байт памяти (x - байты пэдов).

Python работает аналогично (см. документацию struct):

>>> import struct
>>> struct.pack('BHBL',1,2,3,4)
'\x01\x00\x02\x00\x03\x00\x00\x00\x04\x00\x00\x00'
>>> struct.calcsize('BHBL')
12

У компиляторов обычно есть способ устранения заполнения. В Python любое из = < > ! будет устранено заполнение:

>>> struct.calcsize('=BHBL')
8
>>> struct.pack('=BHBL',1,2,3,4)
'\x01\x02\x00\x03\x04\x00\x00\x00'

Остерегайтесь пропускать ручку структуры. В C эти структуры:

struct A {       struct B {
    short a;         int a;
    char b;          char b;
};               };

обычно равны 4 и 8 байтам соответственно. Заполнение происходит в конце структуры, если структуры используются в массиве. Это позволяет членам "a" выравниваться по правильным границам для структур позже в массиве. Структурный модуль Python не заполняется в конце:

>>> struct.pack('LB',1,2)
'\x01\x00\x00\x00\x02'
>>> struct.pack('LBLB',1,2,3,4)
'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04'

Ответ 2

По умолчанию на многих платформах короткий будет выровнен со смещением с кратным 2, поэтому после char будет добавлен байт дополнений.

Чтобы отключить это, используйте: struct.unpack("=BH", data). Это будет использовать стандартное выравнивание, которое не добавляет дополнения:

>>> struct.calcsize('=BH')
3

Символ = будет использовать собственный порядок байтов. Вы также можете использовать < или > вместо = для принудительного упорядочивания байтов младшего или старшего байта соответственно.