我想使用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倍。
我在做或期待发生什么问题吗?
答案 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