繁体   English   中英

在python中将ctypes结构转换为bytearray

[英]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.

请注意,此表示还存储了不相关的最后一个字段(指针)地址(因为也存储了内容)。



更新#0

字符串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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM