在读/写二进制数据结构时访问位域

时间:2011-08-25 23:29:37

标签: python struct ctypes binary-data bit-fields

我正在为二进制格式编写解析器。这种二进制格式涉及不同的表,它们也是二进制格式,通常包含不同的字段大小(介于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种不同类型的此类结构,因此编写代码变得更容易出错。我也担心效率,因为这个工具可能用于解析大千兆字节的二进制数据。

感谢。

2 个答案:

答案 0 :(得分:6)

我没有严格测试过这个,但它似乎适用于无符号类型(编辑:它也适用于带符号的字节/短类型)。

编辑2:这真的很受欢迎。这取决于库的编译器将这些位打包到结构中的方式,这不是标准化的。例如,使用gcc 4.5.3只要我不使用该属性来打包结构,即__attribute__ ((__packed__))(因此,它将被打包成4个字节,而不是6个字节,您可以使用{ {1}}和__alignof__)。我可以通过将sizeof添加到ctypes结构定义来使它几乎工作,但它对于fieldE失败了。 gcc说明:“GCC 4.4中”填充位字段'fieldE'的偏移已经改变。

_pack_ = True

输出:

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])))

C:

fieldA: 3
fieldB: 1
fieldC: 5
fieldD: 12345
fieldE: 9

答案 1 :(得分:4)

使用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库那么快。