如何使用ctypes读取多字节值

时间:2019-07-15 23:48:44

标签: python struct ctypes

我想使用ctypes Structure和Union从一个字节对象读取两个连续的24位值。这将是6个字节。

不幸的是,ctypes读取每个值32位。 另外,sizeof报告的结构比预期的大。

使用_pack_ = 1并没有达到预期的结果。

具有最小结构和二进制示例值的示例:

import unittest
import ctypes

SixBytes = ctypes.c_uint8 * 6

class MultiBits(ctypes.BigEndianStructure):
    _pack_ = 1
    _fields_ = [
        ("x", ctypes.c_uint, 24),
        ("y", ctypes.c_uint, 24),
    ]

class Multi(ctypes.Union):
    _fields_ = [
        ("bits", MultiBits),
        ("asbytes", SixBytes),
    ]

class TestMultibyte(unittest.TestCase):
    def test_size(self):
        self.assertEqual(ctypes.sizeof(MultiBits), 6)

    def test_multibyte(self):
        data = b'\x00\x01\x01\x00\x01\x02'
        parser = Multi()
        parser.asbytes = SixBytes(*data)
        self.assertEqual(parser.bits.x, 257)
        self.assertEqual(parser.bits.y, 258)

我本来希望sizeof(MultiBits)6并且值可以正确解析。

相反,sizeof(MultiBits)被报告为8,而parser.bits.y是预期结果的256倍。

我在做或期待发生什么问题吗?

2 个答案:

答案 0 :(得分:2)

您可以使用int.from_bytes一次解码24位(3个字节)。

data = b'\x00\x01\x01\x00\x01\x02'
[int.from_bytes(data[i:i+3], byteorder='little', signed=False) for i in range(0, len(data), 3)]
>>> [65792, 131328]

整数与您的代码中一样是无符号的,我猜测字节顺序是小端的,尽管看起来您的测试假设是大端的。

答案 1 :(得分:1)

在结构中声明位域时,位域的大小将始终是位域使用的类型的倍数……在这种情况下,c_int(4个字节)。 24位不能容纳4个字节,因此您获得8个字节。您必须坚持使用8位类型以适合24位而无需填充。

这是一种使用属性而不是位域来获取所需内容的方法。顺便说一句,提供单元测试的荣誉!!!我添加了一个以测试属性设置器...

import unittest
import ctypes

SixBytes = ctypes.c_uint8 * 6

class MultiBits(ctypes.BigEndianStructure):
    _fields_ = [("_data", SixBytes)]

    @property
    def x(self):
        return int.from_bytes(self._data[:3],'big')

    @x.setter
    def x(self,value):
        self._data[:3] = value.to_bytes(3,'big')

    @property
    def y(self):
        return int.from_bytes(self._data[3:],'big')

    @y.setter
    def y(self,value):
        self._data[3:] = value.to_bytes(3,'big')

class Multi(ctypes.Union):
    _fields_ = [("bits", MultiBits),
                ("asbytes", SixBytes)]

class TestMultibyte(unittest.TestCase):
    def test_size(self):
        self.assertEqual(ctypes.sizeof(MultiBits), 6)

    def test_multibyte(self):
        data = b'\x00\x01\x01\x00\x01\x02'
        parser = Multi()
        parser.asbytes = SixBytes(*data)
        self.assertEqual(parser.bits.x, 257)
        self.assertEqual(parser.bits.y, 258)

    def test_setbits(self):
        data = b'\x00\x01\x01\x00\x01\x02'
        parser = Multi()
        parser.asbytes = SixBytes(*data)
        parser.bits.x = 0xABCDEF
        parser.bits.y = 0x123456
        self.assertEqual(bytes(parser.asbytes),b'\xab\xcd\xef\x12\x34\x56')

if __name__ == '__main__':
    unittest.main()

输出:

...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK