如何使用 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))一样将它单行复制xy。当然,您可能会有一个变量作为第二个参数,而不是 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)的主要内容,如果未能解决你的问题,请参考以下文章

从R中的列表中打包和解包元素

当我打包和解包浮点数时,如何消除浮点不准确性?

pak文件的打包和解包

java中Socket如何实现数据包传输的打包和解包?

怎么将web工程打包war和解包war

SSE/SSE2 指令的打包和解包数据?