web-dev-qa-db-fra.com

Comment emballer et décompresser à l'aide de ctypes (Structure <-> str)

Cela peut être une question stupide, mais je n'ai pas trouvé de bonne réponse dans les documents ou n'importe où.

Si j'utilise struct pour définir une structure binaire, la structure a 2 méthodes symétriques pour la sérialisation et la désérialisation (pack et unpack) mais il semble ctypes n'a pas de méthode simple pour faire ça. Voici ma solution, qui ne va pas:

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...
41
Mr Temp

Le wiki PythonInfo a une solution pour cela.

FAQ: Comment copier des octets dans Python à partir d'une structure ctypes.Structure?

def send(self):
    return buffer(self)[:]

FAQ: Comment copier des octets dans un ctypes.Structure à partir de Python?

def receiveSome(self, bytes):
    fit = min(len(bytes), ctypes.sizeof(self))
    ctypes.memmove(ctypes.addressof(self), bytes, fit)

Leur send est l'équivalent (plus ou moins) de pack, et receiveSome est une sorte de pack_into. Si vous avez une situation "sûre" où vous décompressez dans une structure du même type que l'original, vous pouvez le placer sur une ligne comme memmove(addressof(y), buffer(x)[:], sizeof(y)) pour copier x dans y. Bien sûr, vous aurez probablement une variable comme deuxième argument, plutôt qu'un emballage littéral de x.

31
Mark Rushakoff

Jetez un œil à ce lien sur les E/S binaires en python:

http://www.dabeaz.com/blog/2009/08/python-binary-io-handling.html

Sur cette base, vous pouvez simplement écrire ce qui suit pour lire à partir d'un tampon (pas seulement des fichiers):

g = open("foo","rb")
q = Example()
g.readinto(q)

Écrire, c'est tout simplement:

g.write(q)

De même pour l'utilisation des sockets:

s.send(q)

et

s.recv_info(q)

J'ai fait quelques tests avec pack/unpack et ctypes et cette approche est la plus rapide, sauf pour écrire directement en C

18
Ralph Paul

Testé sur Python3

e = Example(12, 13)
serialized = bytes(e)
deserialized = Example.from_buffer_copy(serialized)
5
TomDotTom