Listing [Python 3.Docs]: ctypes - библиотека сторонних функций для Python.
Одним из способов может быть создание вспомогательного метода, который выполняет преобразование. Ниже приведен такой пример, который также выполняет преобразование обратно (поскольку я думаю, что преобразование только в одну сторону было бы в значительной степени бесполезным).
code00.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
Предыдущий вариант не работает хорошо, когда есть NUL ( 0 , \ x00 ) char s в строке (text = b"abcd\x001234"
), потому что ctypes.c_char_p используется для NUL завершенных строк . Я думаю, что наличие поля len автоматически подразумевает вышеприведенный сценарий.
code01.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.
Примечания (отличия от предыдущей версии):