Доступ к битовым полям при чтении / записи двоичных структур данных
Я пишу парсер для двоичного формата. Этот двоичный формат включает в себя различные таблицы, которые снова находятся в двоичном формате, содержащем различные размеры полей обычно (где - то между 50-100 из них).
большинство этих структур будут иметь bitfields и будут выглядеть примерно так, когда представлены в C:
struct myHeader
{
unsigned char fieldA : 3
unsigned char fieldB : 2;
unsigned char fieldC : 3;
unsigned short fieldD : 14;
unsigned char fieldE : 4
}
я наткнулся на модуль структуры, но понял, что его самое низкое разрешение было байтом, а не битом, иначе модуль в значительной степени был подходит для этой работы.
Я знаю, что bitfields поддерживаются с помощью ctypes, но я не уверен, как взаимодействовать структуры ctypes, содержащие bitfields здесь.
мой другой вариант-манипулировать битами сам и кормить его в байты и использовать его с модулем struct - но поскольку у меня есть около 50-100 различных типов таких структур, написание кода для этого становится более подверженным ошибкам. Я также беспокоюсь об эффективности, так как этот инструмент может использоваться для анализа больших гигабайт двоичных данных.
спасибо.
2 ответов
используя bitstring (который вы упоминаете, что смотрите) это должно быть достаточно легко реализовать. Сначала, чтобы создать некоторые данные для декодирования:
>>> myheader = "3, 2, 3, 14, 4"
>>> a = bitstring.pack(myheader, 1, 0, 5, 1000, 2)
>>> a.bin
'00100101000011111010000010'
>>> a.tobytes()
'%\x0f\xa0\x80'
и затем декодирование его снова просто
>>> a.readlist(myheader)
[1, 0, 5, 1000, 2]
вашей главной заботой может быть скорость. Библиотека хорошо оптимизирована Python, но это не так быстро, как библиотека C.
Я не проверял это строго, но, похоже, он работает с неподписанными типами (edit: он также работает со подписанными байтами/короткими типами).
Edit 2: это действительно хит или промах. Это зависит от того, как компилятор библиотеки упаковал биты в структуру, которая не стандартизирована. Например, с gcc 4.5.3 он работает до тех пор, пока я не использую атрибут для упаковки структуры, т. е. __attribute__ ((__packed__))
(поэтому вместо 6 байтов он упаковывается в 4 байта, которые вы можете проверить с помощью __alignof__
и sizeof
). Я могу сделать это почти работать, добавив _pack_ = True
к определению структуры ctypes, но это не удается для fieldE. GCC отмечает:"смещение упакованного битового поля "fieldE" изменилось в GCC 4.4".
import ctypes
class MyHeader(ctypes.Structure):
_fields_ = [
('fieldA', ctypes.c_ubyte, 3),
('fieldB', ctypes.c_ubyte, 2),
('fieldC', ctypes.c_ubyte, 3),
('fieldD', ctypes.c_ushort, 14),
('fieldE', ctypes.c_ubyte, 4),
]
lib = ctypes.cdll.LoadLibrary('C/bitfield.dll')
hdr = MyHeader()
lib.set_header(ctypes.byref(hdr))
for x in hdr._fields_:
print("%s: %d" % (x[0], getattr(hdr, x[0])))
выход:
fieldA: 3
fieldB: 1
fieldC: 5
fieldD: 12345
fieldE: 9
C:
typedef struct _MyHeader {
unsigned char fieldA : 3;
unsigned char fieldB : 2;
unsigned char fieldC : 3;
unsigned short fieldD : 14;
unsigned char fieldE : 4;
} MyHeader, *pMyHeader;
int set_header(pMyHeader hdr) {
hdr->fieldA = 3;
hdr->fieldB = 1;
hdr->fieldC = 5;
hdr->fieldD = 12345;
hdr->fieldE = 9;
return(0);
}