[英]Convert ctypes Struct to bytearray in python
有没有办法转换包含指向字节数组的指针的 Ctypes 结构?
class SRamAccess(ctypes.Structure):
_fields_ = [('channel', ctypes.c_uint), ('offset', ctypes.c_uint), ('len', ctypes.c_uint), ('data', ctypes.c_char_p)]
只需将它传递给bytearray()
:
>>> import ctypes
>>> class SRamAccess(ctypes.Structure):
... _fields_ = [('channel', ctypes.c_uint), ('offset', ctypes.c_uint), ('len', ctypes.c_uint), ('data', ctypes.c_char_p)]
...
>>> s = SRamAccess(1,2,3,b'blah')
>>> bytearray(s)
bytearray(b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\xf0 .\x1a\xed\x01\x00\x00')
请注意,'blah' 不是结构的一部分。 最后 8 个字节(64 位 Python)是指针地址,之前的 4 个字节是填充以将 8 字节指针与结构中的 8 字节偏移量对齐。
您需要结构中的数组才能查看数组内容:
>>> class SRamAccess(ctypes.Structure):
... _fields_ = [('channel', ctypes.c_uint), ('offset', ctypes.c_uint), ('len', ctypes.c_uint), ('data', ctypes.c_char * 5)]
...
>>> s = SRamAccess(1,2,3,b'blah')
>>> bytearray(s)
bytearray(b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00blah\x00\x00\x00\x00')
请注意,最后 3 个字节被填充以使整个结构的大小成为 4 个字节的倍数,因此此类结构的数组保持 4 字节整数在 4 字节边界上对齐。
清单[Python 3.Docs]: ctypes - Python 的外部函数库。
一种方法是创建一个执行转换的辅助方法。 下面是这样一个例子,它也进行了转换(我认为只转换一种方式几乎没用)。
代码00.py :
#!/usr/bin/env python
import sys
import ctypes as ct
class SRamAccess(ct.Structure):
_fields_ = [
("channel", ct.c_uint),
("offset", ct.c_uint),
("len", ct.c_uint),
("data", ct.c_char_p),
]
@classmethod
def deserialize(cls, buf):
inst = cls.from_buffer(buf)
return inst
def serialize(self):
struct_size = ct.sizeof(SRamAccess)
size = struct_size + self.len
buf = (ct.c_char * size)()
ct.memmove(ct.addressof(buf), ct.addressof(self), struct_size)
ct.memmove(ct.addressof(buf) + struct_size, self.data, self.len)
return bytearray(buf)
def __str__(self):
s = self.__repr__()
for field_name, _ in self._fields_[:-1]:
s += "\n {0:s}: {1:}".format(field_name, getattr(self, field_name))
s += "\n {0:s}:".format(self._fields_[-1][0])
for i in range(self.len):
s += " 0x{0:02X}".format(self.data[i])
return "{0:s}\n".format(s)
def main(*argv):
text = b"abcd1234"
ssrc = SRamAccess(1, 2, len(text), text)
print("Src:", ssrc)
buf = ssrc.serialize()
print("Buf:", buf)
sdst = SRamAccess.deserialize(buf)
print("\nDst:", sdst)
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main(*sys.argv[1:])
print("\nDone.")
输出:
e:\\Work\\Dev\\StackOverflow\\q060926139>"e:\\Work\\Dev\\VEnvs\\py_pc064_03.07.06_test0\\Scripts\\python.exe" code00.py Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32 Src: <__main__.SRamAccess object at 0x00000298F25553C8> channel: 1 offset: 2 len: 8 data: 0x61 0x62 0x63 0x64 0x31 0x32 0x33 0x34 Buf: bytearray(b'\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x00 \\xc6V\\xf2\\x98\\x02\\x00\\x00abcd1234') Dst: <__main__.SRamAccess object at 0x00000298F2555448> channel: 1 offset: 2 len: 8 data: 0x61 0x62 0x63 0x64 0x31 0x32 0x33 0x34 Done.
请注意,此表示还存储了不相关的最后一个字段(指针)地址(因为也存储了内容)。
当字符串( text = b"abcd\\x001234"
)中有NUL ( 0 , \\x00 ) char时,先前的变体表现不佳,因为ctypes.c_char_p用于NUL终止的字符串。 我认为len字段的存在自动暗示了上述情况。
代码01.py :
#!/usr/bin/env python
import sys
import ctypes as ct
import struct
CharPtr = ct.POINTER(ct.c_char)
class SRamAccess(ct.Structure):
_fields_ = [
("channel", ct.c_uint),
("offset", ct.c_uint),
("len", ct.c_uint),
("data", CharPtr),
]
@classmethod
def deserialize(cls, buf):
fmt_prefix = "II"
data_len = len(buf) - struct.calcsize(fmt_prefix)
fmt = fmt_prefix + "b" * data_len
unpacked = struct.unpack(fmt, buf)
return cls(unpacked[0], unpacked[1], 0, unpacked[2:])
def serialize(self):
buf = struct.pack("II" + "b" * self.len, self.channel, self.offset, *self.data[:self.len])
return bytearray(buf)
def __setattr__(self, name, value):
if name == "data":
self.len = len(value)
buf = (ct.c_char * len(value))(*value)
super().__setattr__(name, buf)
else:
super().__setattr__(name, value)
def __str__(self):
s = self.__repr__()
for field_name, _ in self._fields_[:-1]:
s += "\n {0:s}: {1:}".format(field_name, getattr(self, field_name))
s += "\n {0:s}:".format(self._fields_[-1][0])
for i in range(self.len):
s += " 0x{0:02X}".format(ord(self.data[i]))
return "{0:s}\n".format(s)
def main(*argv):
text = b"abcd\x00123"
ssrc = SRamAccess(1, 2, 12345, text)
print("Src:", ssrc)
buf = ssrc.serialize()
print("Buf:", buf)
sdst = SRamAccess.deserialize(buf)
print("\nDst:", sdst)
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main(*sys.argv[1:])
print("\nDone.")
输出:
e:\\Work\\Dev\\StackOverflow\\q060926139>"e:\\Work\\Dev\\VEnvs\\py_pc064_03.07.06_test0\\Scripts\\python.exe" code01.py Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32 Src: <__main__.SRamAccess object at 0x0000025277C85348> channel: 1 offset: 2 len: 8 data: 0x61 0x62 0x63 0x64 0x00 0x31 0x32 0x33 Buf: bytearray(b'\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00abcd\\x00123') Dst: <__main__.SRamAccess object at 0x0000025277C85448> channel: 1 offset: 2 len: 8 data: 0x61 0x62 0x63 0x64 0x00 0x31 0x32 0x33 Done.
注释(与上一版本相比的变化):
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.