struct — Estrutucturas binarias de datos

Propósito:Convierte entre canedas y datos binarios.

El módulo struct incluye funciones para convertir entre cadenas de bytes y tipos de datos nativos de Python, como números y cadenas.

Funciones frente a clase Struct

Un conjunto de funciones a nivel de módulo está disponible para trabajar con valores estructurados, así como la clase Struct. Especificadores de formato se convierten de su formato de cadena a una representación compilada, similar a la forma en que se manejan las expresiones regulares. La conversión requiere algunos recursos, por lo que suele ser más eficiente hacerla una vez cuando se crea una instancia Struct y llamar métodos en la instancia en lugar de usar las funciones a nivel de módulo. Todos los ejemplos que siguen usan la clase Struct.

Empaquetando y desempaquetando

Structs admiten empaquetar datos en cadenas y desempaquetar datos de cadenas utilizando especificadores de formato compuestos por caracteres que representan el tipo de datos y los indicadores opcionales de conteo y extremidad (endianness). Referete a la documentación estándar de la biblioteca para obtener una lista completa de especificadores de formato compatibles.

En este ejemplo, el especificador solicita un valor entero o entero largo, una cadena de dos bytes y un número de coma flotante. Los espacios en el especificador de formato se incluyen para separar los indicadores de tipo, y son ignorados cuando se compila el formato.

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('Original values:', values)
print('Format string  :', s.format)
print('Uses           :', s.size, 'bytes')
print('Packed Value   :', binascii.hexlify(packed_data))

El ejemplo convierte el valor empaquetado a una secuencia de bytes hexadecimales para imprimir con binascii.hexlify(), ya que algunos de los caracteres son nulos.

$ python3 struct_pack.py

Original values: (1, b'ab', 2.7)
Format string  : b'I 2s f'
Uses           : 12 bytes
Packed Value   : b'0100000061620000cdcc2c40'

Utiliza unpack() para extraer datos de su representación empaquetada.

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('Unpacked Values:', unpacked_data)

Pasar el valor empaquetado a unpack(), devuelve básicamente los mismos valores (ten en cuenta la discrepancia en el valor del punto flotante).

$ python3 struct_unpack.py

Unpacked Values: (1, b'ab', 2.700000047683716)

Extremidad

Por defecto, se codifican los valores utilizando la noción de biblioteca C nativa de extremidad. Es fácil anular esa elección proporcionando una directiva de extremidad explícita en la cadena de formato.

struct_endianness.py
import struct
import binascii

values = (1, 'ab'.encode('utf-8'), 2.7)
print('Original values:', 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('Format string  :', s.format, 'for', name)
    print('Uses           :', s.size, 'bytes')
    print('Packed Value   :', binascii.hexlify(packed_data))
    print('Unpacked Value :', s.unpack(packed_data))

the table below enumera los especificadores de orden de bytes utilizados por Struct.

Especificadores de orden de byte para struct
Código Significado
@ Orden nativo
= Estándar nativo
< little-endian
> big-endian
! Orden de red
$ python3 struct_endianness.py

Original values: (1, b'ab', 2.7)

Format string  : b'@ I 2s f' for native, native
Uses           : 12 bytes
Packed Value   : b'0100000061620000cdcc2c40'
Unpacked Value : (1, b'ab', 2.700000047683716)

Format string  : b'= I 2s f' for native, standard
Uses           : 10 bytes
Packed Value   : b'010000006162cdcc2c40'
Unpacked Value : (1, b'ab', 2.700000047683716)

Format string  : b'< I 2s f' for little-endian
Uses           : 10 bytes
Packed Value   : b'010000006162cdcc2c40'
Unpacked Value : (1, b'ab', 2.700000047683716)

Format string  : b'> I 2s f' for big-endian
Uses           : 10 bytes
Packed Value   : b'000000016162402ccccd'
Unpacked Value : (1, b'ab', 2.700000047683716)

Format string  : b'! I 2s f' for network
Uses           : 10 bytes
Packed Value   : b'000000016162402ccccd'
Unpacked Value : (1, b'ab', 2.700000047683716)

Búferes

Trabajar con datos binarios empaquetados generalmente se reserva para situaciones sensibles al rendimiento o al pasar datos adentro y afuera de módulos de extensión. Estos casos se pueden optimizar evitando la sobrecarga de asignar un nuevo búfer para cada estructura empaquetada. Los métodos pack_into() y unpack_from() admiten la escritura directa a búferes preasignados.

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('Original:', values)

print()
print('ctypes string buffer')

b = ctypes.create_string_buffer(s.size)
print('Before  :', binascii.hexlify(b.raw))
s.pack_into(b, 0, *values)
print('After   :', binascii.hexlify(b.raw))
print('Unpacked:', s.unpack_from(b, 0))

print()
print('array')

a = array.array('b', b'\0' * s.size)
print('Before  :', binascii.hexlify(a))
s.pack_into(a, 0, *values)
print('After   :', binascii.hexlify(a))
print('Unpacked:', s.unpack_from(a, 0))

El atributo size` de Struct nos dice qué tan grande necesita ser el búfer.

$ python3 struct_buffers.py

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

ctypes string buffer
Before  : b'000000000000000000000000'
After   : b'0100000061620000cdcc2c40'
Unpacked: (1, b'ab', 2.700000047683716)

array
Before  : b'000000000000000000000000'
After   : b'0100000061620000cdcc2c40'
Unpacked: (1, b'ab', 2.700000047683716)

Ver también