【问题标题】:Accessing bitfields while reading/writing binary data structures在读/写二进制数据结构时访问位域
【发布时间】:2011-11-04 03:09:47
【问题描述】:

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

谢谢。

【问题讨论】:

  • 还有第 3 方位数组/位操作库。
  • 这将是相当多的工作,但您可能会设计一个类,该类可以将 C 风格的结构定义(或类似它们消除包装歧义的东西)解析为一组掩码,为每个位域,通过struct模块读入数据到字节级别,提供__getattr__访问权限。
  • 是的,我现在遇到了这些工具 - python-bitstringConstructBitReader - 并阅读了他们的文档。 Bit Reader 似乎是一个可行的解决方案,但我看到here 的性能将会大受欢迎。据我所知,从他们的基本文档中可以找到不支持位字段的构造。 Python-bitstring 听起来很有希望,需要深入挖掘
  • 是的,Russell 是我目前的最后一个选择——类似于更高级别的抽象,以支持带有 struct 模块的位域。

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


【解决方案1】:

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

【讨论】:

  • 感谢 Scott - 是的,我检查了您的位串库,它确实非常接近我的要求。事实上,我在邮件列表here 中发布了这个问题。我可以理解它可以作为一个列表来阅读 - 但为了方便代码可读性,我更喜欢使用字典,因为我将要处理的结构很容易有超过 20 或 30 个字段。我知道它在 pack 中受支持,但想知道如何将它与 unpack 一起使用,因为这将是主要功能。
  • @Ash:你还不能解压到字典。我认为您需要像here 提出的解码方法这样的东西,部分原因是我真正想要返回的是有序字典 - 我不确定无序字典是否有用。不过我会再考虑一下...
  • 是的,返回一个有序字典是有意义的,但我猜它的支持只存在于 Python 3.3a0 中(或者至少基于页面上写的 here- PEP372
【解决方案2】:

我没有对此进行严格测试,但它似乎适用于无符号类型(编辑:它也适用于有符号字节/短类型)。

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

【讨论】:

  • Does Python have a bitfield type?查看一个完全不需要任何 C 代码或 dll 的测试示例
  • @nealmcb - 您的示例代表了一种在 Python 本身中存储此类数据的方法。但是如何将这些数据从/导出到可以读取/写入磁盘或可以通过网络接收/发送的字节流?
  • @ash 这就是联合的​​用途,以及该示例中的 flags.asbyte 字段。感谢您指出它不是很清楚。我已经对那里的文字进行了润色,以使其更加清晰。呵呵:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多