来自缓冲区的 ctypes - Python
ctypes from buffer - Python
我正在使用 ctypes 从二进制数据缓冲区进行转换
log = DataFromBuffe.from_buffer(buffer)
在我的 class 我有
class DataFromBuffe(ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [
('id', ctypes.c_char * 1),
('name', ctypes.c_char * 30),
('value', ctypes.c_double),
('size', ctypes.c_uint16),
('date', type(datetime.datetime))
]
但是我有两个问题?
1 - 如何使用日期时间?字段 'date' 不工作。
2 - 字段 'size',由于某种原因是 BigEndian。是否可以仅针对此字段更改结构?
1 - How can I work with datetime? Fild 'date' is not working.
您的 date
字段 必须 是 ctypes
类型(或从 ctypes
类型继承的类型)。这意味着你必须找到一种方法将日期表示为数字(int、float、double,任何你想要的,但它不能是非 ctypes python 类型)。
在此示例中,我使用了众所周知的 Unix Epoch(可以用 ctypes.c_uint32
表示)
class DataFromBuffer(ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [
('id', ctypes.c_char * 1),
('name', ctypes.c_char * 30),
('value', ctypes.c_double),
('size', ctypes.c_uint16),
('date', ctypes.c_uint32), # date as a 32-bit unsigned int.
]
# snip
now_date_time = datetime.datetime.now()
now_int = int(now_date_time.timestamp()) # now as an integer (seconds from the unix epoch)
print(f"Now - datetime: {now_date_time!s}; int: {now_int}")
test_buffer = (b"A" + # id
# snip
now_int.to_bytes(4, "little") # date
)
至于日期时间的转换,我只是在结构中添加了一个函数成员,这样它就可以将日期 (a ctypes.c_uint32
) 转换为日期时间:
def date_to_datetime(self) -> datetime.datetime:
"""Get the date field as a python datetime.
"""
return datetime.datetime.fromtimestamp(self.date)
2 - Field 'size', for some reason is BigEndian. Is it possible change structure just for this field?
不,这不可能。一种可能的方法是使用一个函数或 属性 来访问您想要的字段(在后台执行某种转换):
def real_size(self) -> int:
"""Get the correct value for the size field (endianness conversion).
"""
# note: there multiple way of doing this: bytearray.reverse() or struct.pack and unpack, etc.
high = self.size & 0xff
low = (self.size & 0xff00) >> 8
return high | low
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import ctypes
import math
import datetime
class DataFromBuffer(ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [
('id', ctypes.c_char * 1),
('name', ctypes.c_char * 30),
('value', ctypes.c_double),
('size', ctypes.c_uint16),
('date', ctypes.c_uint32),
]
def date_to_datetime(self) -> datetime.datetime:
"""Get the date field as a python datetime.
"""
return datetime.datetime.fromtimestamp(self.date)
def real_size(self) -> int:
"""Get the correct value for the size field (endianness conversion).
"""
# note: there multiple way of doing this: bytearray.reverse() or struct.pack and unpack, etc.
high = self.size & 0xff
low = (self.size & 0xff00) >> 8
return high | low
if __name__ == '__main__':
name = b"foobar"
now_date_time = datetime.datetime.now()
now_int = int(now_date_time.timestamp()) # now as an integer (seconds from the unix epoch)
print(f"Now - datetime: {now_date_time!s}; int: {now_int}")
test_buffer = (b"A" + # id
name + (30 - len(name)) * b"\x00" + # name (padded with needed \x00)
bytes(ctypes.c_double(math.pi)) + # PI as double
len(name).to_bytes(2, "big") + # size (let's pretend it's the name length)
now_int.to_bytes(4, "little") # date (unix epoch)
)
assert ctypes.sizeof(DataFromBuffer) == len(test_buffer)
data = DataFromBuffer.from_buffer(bytearray(test_buffer))
print(f"date: {data.date}; as datetime: {data.date_to_datetime()}")
print(f"size: {data.size} ({data.size:#x}); real size: {data.real_size()} ({data.real_size():#x})")
输出:
Now - datetime: 2019-07-31 14:52:21.193023; int: 1564577541
date: 1564577541; as datetime: 2019-07-31 14:52:21
size: 1536 (0x600); real size: 6 (0x6)
我正在使用 ctypes 从二进制数据缓冲区进行转换
log = DataFromBuffe.from_buffer(buffer)
在我的 class 我有
class DataFromBuffe(ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [
('id', ctypes.c_char * 1),
('name', ctypes.c_char * 30),
('value', ctypes.c_double),
('size', ctypes.c_uint16),
('date', type(datetime.datetime))
]
但是我有两个问题?
1 - 如何使用日期时间?字段 'date' 不工作。
2 - 字段 'size',由于某种原因是 BigEndian。是否可以仅针对此字段更改结构?
1 - How can I work with datetime? Fild 'date' is not working.
您的 date
字段 必须 是 ctypes
类型(或从 ctypes
类型继承的类型)。这意味着你必须找到一种方法将日期表示为数字(int、float、double,任何你想要的,但它不能是非 ctypes python 类型)。
在此示例中,我使用了众所周知的 Unix Epoch(可以用 ctypes.c_uint32
表示)
class DataFromBuffer(ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [
('id', ctypes.c_char * 1),
('name', ctypes.c_char * 30),
('value', ctypes.c_double),
('size', ctypes.c_uint16),
('date', ctypes.c_uint32), # date as a 32-bit unsigned int.
]
# snip
now_date_time = datetime.datetime.now()
now_int = int(now_date_time.timestamp()) # now as an integer (seconds from the unix epoch)
print(f"Now - datetime: {now_date_time!s}; int: {now_int}")
test_buffer = (b"A" + # id
# snip
now_int.to_bytes(4, "little") # date
)
至于日期时间的转换,我只是在结构中添加了一个函数成员,这样它就可以将日期 (a ctypes.c_uint32
) 转换为日期时间:
def date_to_datetime(self) -> datetime.datetime:
"""Get the date field as a python datetime.
"""
return datetime.datetime.fromtimestamp(self.date)
2 - Field 'size', for some reason is BigEndian. Is it possible change structure just for this field?
不,这不可能。一种可能的方法是使用一个函数或 属性 来访问您想要的字段(在后台执行某种转换):
def real_size(self) -> int:
"""Get the correct value for the size field (endianness conversion).
"""
# note: there multiple way of doing this: bytearray.reverse() or struct.pack and unpack, etc.
high = self.size & 0xff
low = (self.size & 0xff00) >> 8
return high | low
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import ctypes
import math
import datetime
class DataFromBuffer(ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [
('id', ctypes.c_char * 1),
('name', ctypes.c_char * 30),
('value', ctypes.c_double),
('size', ctypes.c_uint16),
('date', ctypes.c_uint32),
]
def date_to_datetime(self) -> datetime.datetime:
"""Get the date field as a python datetime.
"""
return datetime.datetime.fromtimestamp(self.date)
def real_size(self) -> int:
"""Get the correct value for the size field (endianness conversion).
"""
# note: there multiple way of doing this: bytearray.reverse() or struct.pack and unpack, etc.
high = self.size & 0xff
low = (self.size & 0xff00) >> 8
return high | low
if __name__ == '__main__':
name = b"foobar"
now_date_time = datetime.datetime.now()
now_int = int(now_date_time.timestamp()) # now as an integer (seconds from the unix epoch)
print(f"Now - datetime: {now_date_time!s}; int: {now_int}")
test_buffer = (b"A" + # id
name + (30 - len(name)) * b"\x00" + # name (padded with needed \x00)
bytes(ctypes.c_double(math.pi)) + # PI as double
len(name).to_bytes(2, "big") + # size (let's pretend it's the name length)
now_int.to_bytes(4, "little") # date (unix epoch)
)
assert ctypes.sizeof(DataFromBuffer) == len(test_buffer)
data = DataFromBuffer.from_buffer(bytearray(test_buffer))
print(f"date: {data.date}; as datetime: {data.date_to_datetime()}")
print(f"size: {data.size} ({data.size:#x}); real size: {data.real_size()} ({data.real_size():#x})")
输出:
Now - datetime: 2019-07-31 14:52:21.193023; int: 1564577541
date: 1564577541; as datetime: 2019-07-31 14:52:21
size: 1536 (0x600); real size: 6 (0x6)