struct - Strutture Dati Binari

Scopo: Converte tra stringhe e dati binari

Il modulo struct include funzioni per la conversione tra stringhe di byte e tipi dati nativi di Python come numeri e stringhe.

Funzioni Contro la Classe Struct

E' disponibile per lavorare con valori strutturati sia un insieme di funzioni a livello di modulo che la classe Struct. Gli specificatori di formato sono convertiti dal loro formato stringa in una rappresentazione compilata, simile al modo nel quale sono gestite le espressioni regolari. La conversione richiede qualche risorsa, quindi è tipicamente più efficiente farlo una sola volta quando si crea l'istanza di Struct, poi chiamare i metodi sull'istanza invece che usare le funzioni a livello di modulo. Tutti gli esempi che seguono utilizzano la classe Struct.

Impacchettare e Spacchettare

Le struttura supportano l'impacchettamento dei dati in stringhe, e lo spacchettamento di dati da stringhe usando specificatori di formato composti da caratteri che rappresentano il tipo di dato e indicatori opzionali di contatori ed endianness. Si faccia riferimento alla documentazione della libreria standard per un elenco completo degli specificatori di formato supportati.

In questo esempio, lo specificatore chiama un valore intero o intero lungo, una stringa di due byte e un numero a virgola mobile. Gli spazi nello specificatore di formato sono inclusi per separare gli indicatori di tipo, e vengono ignorati quanto il formato viene compilato.

# struct_pack.py

import struct
import binascii

values = (1, 'ab'.encode('utf-8'), 2.7)
s = struct.Struct('I 2s f')
packed_data = s.pack(*values)

print('Valori originali    :', values)
print('Formato stringa     :', s.format)
print('Usa                 :', s.size, 'byte')
print('Valore impacchettato:', binascii.hexlify(packed_data))

Questo esempio converte il valore impacchettato in una sequenza di byte esadecimali per la stampa con binascii.hexlify, visto che alcuni caratteri sono null.

$ python3 struct_pack.py

Valori originali    : (1, b'ab', 2.7)
Formato stringa     : I 2s f
Usa                 : 12 byte
Valore impacchettato: b'0100000061620000cdcc2c40'

Si usi unpack() per estrarre dati dalla loro rappresentazione impacchettata.

# struct_unpack.py

import struct
import binascii

packed_data = binascii.unhexlify(b'0100000061620000cdcc2c40')

s = struct.Struct('I 2s f')
unpacked_data = s.unpack(packed_data)
print('Valori spacchettati:', unpacked_data)

In pratica, passando il valore impacchettato a unpack() vengono restituiti gli stessi valori (si noti la discrepanza nel valore a virgola mobile).

$ python3 struct_unpack.py

Valori spacchettati: (1, b'ab', 2.700000047683716)

Endianness

Nella modalità predefinita, i valori sono codificati usando la nozione di endianness della libreria C nativa. E' facile annullare questa scelta fornendo una direttiva di endianness esplicita nella stringa di formato.

# struct_endianness.py

import struct
import binascii

values = (1, 'ab'.encode('utf-8'), 2.7)
print('Valori originali    :', values)

endianness = [
    ('@', 'native, native'),
    ('=', 'native, standard'),
    ('<', 'little-endian'),
    ('>', 'big-endian'),
    ('!', 'network'),
]

for code, name in endianness:
    s = struct.Struct(code + ' I 2s f')
    packed_data = s.pack(*values)
    print()
    print('Formato stringa     :', s.format, 'per', name)
    print('Usa                 :', s.size, 'byte')
    print('Valore impacchettato:', binascii.hexlify(packed_data))
    print('Valore spacchettato :', s.unpack(packed_data))

La tabella qui sotto elenca gli specificatori di ordine dei byte usati da Struct.

CODICE SIGNIFICATO
@ Ordine nativo
= Standard nativo
< little-endian
> big-endian
! Ordine network
$ python3 struct_endianness.py

Valori originali    : (1, b'ab', 2.7)

Formato stringa     : @ I 2s f per native, native
Usa                 : 12 byte
Valore impacchettato: b'0100000061620000cdcc2c40'
Valore spacchettato : (1, b'ab', 2.700000047683716)

Formato stringa     : = I 2s f per native, standard
Usa                 : 10 byte
Valore impacchettato: b'010000006162cdcc2c40'
Valore spacchettato : (1, b'ab', 2.700000047683716)

Formato stringa     : < I 2s f per little-endian
Usa                 : 10 byte
Valore impacchettato: b'010000006162cdcc2c40'
Valore spacchettato : (1, b'ab', 2.700000047683716)

Formato stringa     : > I 2s f per big-endian
Usa                 : 10 byte
Valore impacchettato: b'000000016162402ccccd'
Valore spacchettato : (1, b'ab', 2.700000047683716)

Formato stringa     : ! I 2s f per network
Usa                 : 10 byte
Valore impacchettato: b'000000016162402ccccd'
Valore spacchettato : (1, b'ab', 2.700000047683716)

Buffer

Il lavoro con dati binari impacchettati è tipicamente riservato per situazioni sensibili alle prestazioni o per passare dati da/per moduli di estensione. In questi casi è possibile eseguire una ottimizzazione evitando il sovraccarico dell'allocazione di un nuovo buffer per ogni struttura impacchettata. I metodi pack_into() e unpack_from() supportano la scrittura diretta verso buffer pre allocati.

# struct_buffers.py

import array
import binascii
import ctypes
import struct

s = struct.Struct('I 2s f')
values = (1, 'ab'.encode('utf-8'), 2.7)
print('Originale  :', values)

print()
print('buffer stringhe ctype')

b = ctypes.create_string_buffer(s.size)
print('Prima       :', binascii.hexlify(b.raw))
s.pack_into(b, 0, *values)
print('Dopo        :', binascii.hexlify(b.raw))
print('Spacchettati:', s.unpack_from(b, 0))

print()
print('array')

a = array.array('b', b'\0' * s.size)
print('Prima        :', binascii.hexlify(a))
s.pack_into(a, 0, *values)
print('Dopo         :', binascii.hexlify(a))
print('Spacchettati:', s.unpack_from(a, 0))

L'attributo size di Struct mostra quanto grande deve essere il buffer.

$ python3 struct_buffers.py

Originale  : (1, b'ab', 2.7)

buffer stringhe ctype
Prima       : b'000000000000000000000000'
Dopo        : b'0100000061620000cdcc2c40'
Spacchettati: (1, b'ab', 2.700000047683716)

array
Prima        : b'000000000000000000000000'
Dopo         : b'0100000061620000cdcc2c40'
Spacchettati: (1, b'ab', 2.700000047683716)

Vedere anche:

struct
La documentazione della libreria standard per questo modulo.
Note di portabilità per questo modulo
array
Il modulo array è per lavorare con sequenze di valori di tipo prefissato.
binascii
Il modulo binascii produce rappresentazioni ASCII di dati binari
Endiannes (Wikipedia)
Spiegazione dell'ordine dei byte ed endianness nella codifica.