访问二进制数据结构中的位域

13 投票
2 回答
7483 浏览
提问于 2025-04-17 00:22

我正在写一个解析器,用来处理一种二进制格式。这种二进制格式包含不同的表格,而这些表格也是二进制格式的,里面的字段大小各不相同,通常有50到100个字段。

这些结构大多数会有位域,表示成C语言的样子大概是这样的:

struct myHeader
{
  unsigned char fieldA : 3
  unsigned char fieldB : 2;
  unsigned char fieldC : 3;
  unsigned short fieldD : 14;
  unsigned char fieldE : 4
}

我发现了一个叫做struct的模块,但我意识到它的最小单位是字节,而不是位。要是这个模块能处理位就好了,因为它基本上很适合我的工作。

我知道使用ctypes可以支持位域,但我不太确定怎么在这里使用ctypes结构体来处理位域。

我还有一个选择,就是自己处理这些位,然后把它们放进字节里,再用struct模块来使用。但因为我有差不多50到100种不同类型的结构,这样写代码就容易出错。而且我还担心效率,因为这个工具可能要用来解析大量的二进制数据,可能会有几个GB。

谢谢。

2 个回答

6

我没有进行严格的测试,但看起来这个方法在无符号类型上有效(补充一下,它在有符号的字节和短整型上也有效)。

补充2:这个方法的效果真的有点不稳定。它取决于库的编译器是如何把数据打包到结构体里的,而这个打包方式并没有统一标准。比如,在使用gcc 4.5.3时,只要我不使用打包属性__attribute__ ((__packed__)),它就能正常工作(这样的话,数据就会被打包成4个字节,而不是6个字节,你可以通过__alignof__sizeof来检查)。我可以通过在ctypes结构定义中添加_pack_ = True来让它几乎正常工作,但对于fieldE这个字段就不行了。gcc的说明是:“在GCC 4.4中,打包位域‘fieldE’的偏移量发生了变化”。

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);
}
7

使用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语言写的库。

撰写回答