Доступ к битовым полям при чтении / записи двоичных структур данных

Я пишу парсер для двоичного формата. Этот двоичный формат включает в себя различные таблицы, которые снова находятся в двоичном формате, содержащем различные размеры полей обычно (где - то между 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);
}