Python readinto:如何从array.array转换为自定义ctype结构

时间:2016-08-26 08:41:59

标签: python arrays ctypes python-2.x

我创建了一个整数数组,我希望它们能够被我创建的结构定义解释

from ctypes import *
from array import array

class MyStruct(Structure):
    _fields_ = [("init", c_uint),
                ("state", c_char),
                ("constant", c_int),
                ("address", c_uint),
                ("size", c_uint),
                ("sizeMax", c_uint),
                ("start", c_uint),
                ("end", c_uint),
                ("timestamp", c_uint),
                ("location", c_uint),
                ("nStrings", c_uint),
                ("nStringsMax", c_uint),
                ("maxWords", c_uint),
                ("sizeFree", c_uint),
                ("stringSizeMax", c_uint),
                ("stringSizeFree", c_uint),
                ("recordCount", c_uint),
                ("categories", c_uint),
                ("events", c_uint),
                ("wraps", c_uint),
                ("consumed", c_uint),
                ("resolution", c_uint),
                ("previousStamp", c_uint),
                ("maxTimeStamp", c_uint),
                ("threshold", c_uint),
                ("notification", c_uint),
                ("version", c_ubyte)]

# arr = array.array('I', [1])
# How can I do this?
# mystr = MyStruct(arr) magic
# (mystr.helloworld == 1) == True

我可以做以下事情:

mystr = MyStruct()
rest = array.array('I')
with open('myfile.bin', 'rb') as binaryFile:
    binaryFile.readinto(mystr)
    rest.fromstring(binaryFile.read())

# Now create another struct with rest
rest.readinto(mystr) # Does not work

如果数据包含在array.array('I')中,如何避免使用文件将Ints数组转换为结构?我不确定Structure构造函数接受什么或readinto如何工作。

2 个答案:

答案 0 :(得分:1)

解决方案#1:为单行初始化进行星形解包

Star-unpacking将起作用,但前提是结构中的所有字段都是整数类型。在Python 2.x中,c_char无法从int初始化(它在3.5中工作正常)。如果您将state的类型更改为c_byte,那么您可以执行以下操作:

mystr = MyStruct(*myarr)

这实际上并没有受益于任何array特定魔法(在解压缩步骤中,这些值会暂时转换为Python int,因此您不会减少峰值内存使用量),如果初始化所述array比直接读入结构更容易,那么你只需要array

如果你去明星解包路线,那么阅读.state现在会得到int值,而不是len {1}}值。如果您要使用str进行初始化,但只读取一个字符int,则可以使用str中包含的受保护名称:

property

通过定义自己转换了class MyStruct(Structure): _fields_ = [... ("_state", c_byte), # "Protected" name int-like; constructor expects int ...] @property def state(self): return chr(self._state) @state.setter def state(self, x): if isinstance(x, basestring): x = ord(x) self._state = x 参数传递的property,可以在没有__init__的情况下使用类似的技术:

state

解决方案#2:直接class MyStruct(Structure): _fields_ = [("init", c_uint), ("state", c_char), ...] def __init__(self, init=0, state=b'\0', *args, **kwargs): if not isinstance(state, basestring): state = chr(state) super(MyStruct, self).__init__(init, state, *args, **kwargs) - 类似于减少临时工作的解决方案

您可以使用某些memcpy特定魔法来避免临时Python级别array(并且无需将int更改为state)而无需真正的文件对象使用伪(内存中)文件类对象:

c_byte

这只能起作用,因为您的非import io mystr = MyStruct() # Default initialize # Use BytesIO to gain the ability to write the raw bytes to the struct # because BytesIO's readinto isn't finicky about exact buffer formats io.BytesIO(myarr.tostring()).readinto(mystr) # In Python 3, where array implements the buffer protocol, you can simplify to: io.BytesIO(myarr).readinto(mystr) # This still performs two memcpys (one occurs internally in BytesIO), but # it's faster by avoiding a Python level method call 宽度属性后跟c_int宽度属性(因此它们无论如何都要填充到四个字节);如果你有两个c_int / c_ubyte /等。类型背靠背,然后你有问题(因为c_char的一个值会初始化结构中的两个字段,这似乎不是你想要的。)

如果您使用的是Python 3,那么您可以从array特定魔法中受益,以避免解包和array技术的两步memcpy的成本(来自{{1} } - > BytesIO - > struct)。它在Py3中工作,因为Py3的array类型支持缓冲协议(它在Py2中没有),并且因为Py3' bytes具有array允许您更改memoryview格式以使其与cast直接兼容的方法:

memoryview

array解决方案一样,这只能起作用,因为你的字段碰巧填充到四个字节大小

效果

性能方面,明星解包赢得少量字段,但对于大量字段(您的案例有几十个),基于直接mystr = MyStruct() # Default initialize # Make a view on mystr's underlying memory that behaves like a C array of # unsigned ints in native format (matching array's type code) # then perform a "memcpy" like operation using empty slice assignment # to avoid creating any Python level values. memoryview(mystr).cast('B').cast('I')[:] = myarr 的方法胜出;在23个字段类的测试中,BytesIO解决方案赢得了我的Python 2.7安装上的星形解包因子2.5倍(星形解包为2.5微秒,memcpy为1微秒)。

BytesIO解决方案与BytesIO解决方案类似,但从3.5开始,它比memoryview方法略慢(可能是需要构建的结果)几个临时BytesIO来执行必要的转换操作和/或BytesIO切片分配代码是许多可能格式的通用目的,所以它在实现中不简单memoryviewmemoryview可能会针对更大的副本进行更好的扩展(如果损失是由于固定的memcpy开销造成的),但是您很少有足够重要的结构;它只会出现在memoryview可能获胜的更通用的复制场景(来往cast数组之类)。

答案 1 :(得分:0)

这必须是一个数组吗?你可以使用一个清单吗?您可以从列表中解压缩到可以使用*运算符的函数:

mystr = MyStruct(* arr)

或dict:

mystr = MyStruct(** arr)