如何使用 ctypes 打包和解包(结构 <-> str)
Posted
技术标签:
【中文标题】如何使用 ctypes 打包和解包(结构 <-> str)【英文标题】:How to pack and unpack using ctypes (Structure <-> str) 【发布时间】:2010-12-21 23:52:34 【问题描述】:这可能是一个愚蠢的问题,但我在文档或任何地方都找不到好的答案。
如果我使用 struct 定义二进制结构,则该结构有 2 种对称的序列化和反序列化方法(打包和解包),但似乎 ctypes 没有一个简单的方法来做到这一点。这是我的解决方案,感觉不对:
from ctypes import *
class Example(Structure):
_fields_ = [
("index", c_int),
("counter", c_int),
]
def Pack(ctype_instance):
buf = string_at(byref(ctype_instance), sizeof(ctype_instance))
return buf
def Unpack(ctype, buf):
cstring = create_string_buffer(buf)
ctype_instance = cast(pointer(cstring), POINTER(ctype)).contents
return ctype_instance
if __name__ == "__main__":
e = Example(12, 13)
buf = Pack(e)
e2 = Unpack(Example, buf)
assert(e.index == e2.index)
assert(e.counter == e2.counter)
# note: for some reason e == e2 is False...
【问题讨论】:
这看起来对我来说是正确的。 ctypes 不是用于序列化的,所以你可以用 7 行代码来完成这一事实实际上看起来很不错。 【参考方案1】:The PythonInfo wiki 对此有解决方案。
常见问题解答:如何将字节从 ctypes.Structure 复制到 Python?
def send(self): return buffer(self)[:]
常见问题解答:如何从 Python 将字节复制到 ctypes.Structure?
def receiveSome(self, bytes): fit = min(len(bytes), ctypes.sizeof(self)) ctypes.memmove(ctypes.addressof(self), bytes, fit)
他们的send
(或多或少)相当于pack
,而receiveSome
有点像pack_into
。如果你有一个“安全”的情况,你正在解压到一个与原始结构相同类型的结构中,你可以像memmove(addressof(y), buffer(x)[:], sizeof(y))
一样将它单行复制x
到y
。当然,您可能会有一个变量作为第二个参数,而不是 x
的文字包装。
【讨论】:
我测试了这个解决方案,它也能正常工作。对我来说更重要的是,你发现了一个官方的 python.org 实体(wiki 中的常见问题解答已经足够好了)声明破解它是要走的路。我只是觉得这两个函数/方法必须已经在 ctypes.py 中的某个地方,所以使用指针来破解它似乎非常不合 Python。我知道有些人说 ctypes 不是为序列化等而构建的,但我更喜欢 ctypes OOP-ness 而不是 perl-ish struct 模块。 没有buffer
的Python3呢?如果您尝试简单地将其替换为memoryview
,您将得到TypeError: invalid indexing of 0-dim memory
在 Python3 中,您可以使用 bytes(self)
提取结构字节。【参考方案2】:
看看这个关于 python 中二进制 i/o 的链接:
http://www.dabeaz.com/blog/2009/08/python-binary-io-handling.html
基于此,您可以简单地编写以下内容以从缓冲区(不仅仅是文件)读取:
g = open("foo","rb")
q = Example()
g.readinto(q)
写起来很简单:
g.write(q)
使用套接字也一样:
s.send(q)
和
s.recv_into(q)
我用 pack/unpack 和 ctypes 做了一些测试,除了直接用 C 编写之外,这种方法是最快的
【讨论】:
在 2.6+ 中,更通用的pack
只是 bytearray(q)
,它也使用缓冲区协议。对于通用解包 2.6 还添加了,例如,Example.from_buffer(buf)
如果buf
是可变的,否则Example.from_buffer_copy(buf)
。【参考方案3】:
在 Python3 上测试
e = Example(12, 13)
serialized = bytes(e)
deserialized = Example.from_buffer_copy(serialized)
【讨论】:
以上是关于如何使用 ctypes 打包和解包(结构 <-> str)的主要内容,如果未能解决你的问题,请参考以下文章