来自缓冲区的 ctypes - Python

Posted

技术标签:

【中文标题】来自缓冲区的 ctypes - Python【英文标题】:ctypes from buffer - Python 【发布时间】:2019-07-30 21:32:27 【问题描述】:

我正在使用 ctypes 从二进制数据缓冲区转换

log = DataFromBuffe.from_buffer(buffer)

在我的课上

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 - 我如何使用日期时间?字段“日期”不起作用。

2 - 字段“大小”,出于某种原因是 BigEndian。是否可以仅针对该字段更改结构?

【问题讨论】:

【参考方案1】:

1 - 我如何使用日期时间?字段“日期”不起作用。

您的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
    )

至于转换为日期时间,我只是在结构中添加了一个函数成员,以便它可以将日期(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 - 字段“大小”,出于某种原因是 BigEndian。是否可以仅针对该字段更改结构?

不,这是不可能的。一种可能的方法是使用函数或属性来访问所需的字段(在后台执行某种转换):

    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 - Python的主要内容,如果未能解决你的问题,请参考以下文章

使用 numpy/ctypes 公开 C 分配的内存缓冲区的更安全方法?

libpng 从内存缓冲区加载文件

python使用C函数返回的指针

将 ctype 字节数组转换为字节

NASM ctypes SIMD - 如何访问返回到ctypes的128位数组?

ctype.h字符函数和字符串