我正在编写一个二进制格式的解析器。这种二进制格式涉及到不同的表,这些表也是二进制格式,通常包含不同的字段大小(大约在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的二进制数据。
谢谢。
使用bitstring(您提到您正在查看)应该很容易实现。首先创建一些要解码的数据:
>>> myheader = "3, 2, 3, 14, 4"
>>> a = bitstring.pack(myheader, 1, 0, 5, 1000, 2)
>>> a.bin
'00100101000011111010000010'
>>> a.tobytes()
'%x0fxa0x80'
然后再解码成
>>> a.readlist(myheader)
[1, 0, 5, 1000, 2]
你主要关心的可能是速度。该库是经过良好优化的Python,但速度远不如C库。
我还没有严格测试过,但它似乎适用于无符号类型(编辑:它也适用于有符号字节/短类型)。
编辑2:这真的是命中或错过。它取决于库的编译器将位打包到结构体中的方式,这是不标准化的。例如,在gcc 4.5.3中,只要我不使用属性来打包结构体(即__attribute__ ((__packed__))
),它就可以工作(因此,它不是6字节,而是被打包成4字节,您可以检查__alignof__
和sizeof
)。通过将_pack_ = True
添加到ctypes结构定义中,我可以使其几乎工作,但是对于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);
}