struct en Python: datos binarios

Publicado el: 17/08/2026
Tempo de leitura: 4 minutos
Chic portrait of a woman wearing trendy sunglasses reflecting numbers, captured in a modern setting.

El módulo struct en Python convierte enteros, floats, booleanos y secuencias de bytes en estructuras binarias compactas. También realiza la operación inversa: interpreta bytes recibidos desde archivos, dispositivos o conexiones de red según una cadena de formato.

Es útil para protocolos, cabeceras de archivos, integración con C, sensores y formatos antiguos. El principal riesgo es depender de la plataforma. Sin prefijo, struct usa orden de bytes, tamaños C y alineación nativos. Para intercambiar datos, define explícitamente endianness y tamaños.

Empaqueta y desempaqueta

import struct

formato = ">Ih"
paquete = struct.pack(formato, 1_000_000, -12)
identificador, temperatura = struct.unpack(formato, paquete)

print(paquete.hex())
print(identificador, temperatura)

> selecciona big-endian con tamaños estándar. I es un entero sin signo de 32 bits y h uno con signo de 16 bits. unpack() siempre devuelve una tupla.

Prefijos de orden de bytes

  • <: little-endian, tamaños estándar y sin alineación implícita.
  • >: big-endian con tamaños estándar.
  • !: orden de red, equivalente a big-endian.
  • =: orden nativo con tamaños estándar.
  • @: orden, tamaño y alineación completamente nativos.
import struct

print(struct.pack(">H", 1023).hex())  # 03ff
print(struct.pack("<H", 1023).hex())  # ff03

Para archivos y protocolos, evita el modo nativo implícito. Elige <, > o ! para obtener el mismo layout en todas las arquitecturas.

Calcula el tamaño necesario

import struct

CABECERA = struct.Struct("!4sBBHI")
print(CABECERA.size)

with open("mensaje.bin", "rb") as archivo:
    bruto = archivo.read(CABECERA.size)
    if len(bruto) != CABECERA.size:
        raise ValueError("cabecera incompleta")
    magia, version, flags, tipo, longitud = CABECERA.unpack(bruto)

unpack() necesita un buffer de tamaño exacto. unpack_from() requiere al menos el tamaño del formato después del offset. Valida antes de interpretar.

Reutiliza un Struct compilado

import struct

REGISTRO = struct.Struct("<Iff?")
payload = REGISTRO.pack(42, 18.5, 70.25, True)
identificador, x, y, activo = REGISTRO.unpack(payload)

El módulo mantiene una caché de formatos recientes, pero los objetos con nombre documentan mejor el protocolo y reducen repetición.

Caracteres frecuentes

  • b/B: entero de 8 bits con o sin signo.
  • h/H: entero de 16 bits.
  • i/I: entero estándar de 32 bits.
  • q/Q: entero de 64 bits.
  • e, f, d: float de 16, 32 y 64 bits.
  • ?: booleano.
  • s: bytes de longitud fija.
  • x: byte de padding.

Python 3.14 añadió F y D para números complejos de precisión simple y doble.

Bytes de longitud fija

import struct

FORMATO = struct.Struct("!10sI")
bruto = FORMATO.pack(b"sensor-1", 123)
nombre, lectura = FORMATO.unpack(bruto)
nombre = nombre.rstrip(b"\x00").decode("ascii")

10s es un único campo de diez bytes. Una entrada mayor se trunca y una menor se rellena con ceros. Valida la longitud si el truncamiento sería una pérdida de datos.

Escribe en buffers existentes

import struct

buffer = bytearray(1024)
CABECERA = struct.Struct("!IHH")
CABECERA.pack_into(buffer, 0, 900, 2, 7)
identificador, version, flags = CABECERA.unpack_from(buffer, 0)

Estos métodos aceptan objetos del protocolo de buffer, como bytearray y memoryview, reduciendo asignaciones temporales.

Interpreta registros repetidos

import struct

REGISTRO = struct.Struct("<Ih")
datos = b"".join([
    REGISTRO.pack(1, 20),
    REGISTRO.pack(2, 25),
    REGISTRO.pack(3, 18),
])

for identificador, valor in REGISTRO.iter_unpack(datos):
    print(identificador, valor)

La longitud total debe ser múltiplo del tamaño del registro. Rechaza bytes sobrantes porque pueden indicar corrupción o una versión distinta.

Protocolos con longitud

import struct

HEADER = struct.Struct("!4sBI")
MAX_PAYLOAD = 10 * 1024 * 1024

cabecera = recibir_exactamente(HEADER.size)
magia, version, longitud = HEADER.unpack(cabecera)

if magia != b"APP1" or version != 1:
    raise ValueError("protocolo no soportado")
if longitud > MAX_PAYLOAD:
    raise ValueError("payload demasiado grande")

payload = recibir_exactamente(longitud)

Nunca reserves memoria únicamente con una longitud enviada por el cliente. Aplica máximo, timeout, rate limit y límite de mensajes.

Red y cuerpos comprimidos

Usa ! para orden de red. El cuerpo puede comprimirse con zlib en Python, pero la cabecera debe identificar versión, flags, algoritmo y límites. Los valores internos descritos en opcode en Python no son identificadores estables de protocolo.

Alineación nativa

Con @, el compilador C puede insertar padding. Es útil para reflejar una struct C en el mismo entorno, pero no para almacenamiento portátil.

import struct

print(struct.calcsize("@ci"))
print(struct.calcsize("@ic"))
print(struct.calcsize("=ci"))

El orden de los campos puede cambiar el tamaño. En modos estándar, el padding solo aparece si se declara con x.

Rangos y struct.error

import struct

try:
    struct.pack("!h", 100_000)
except struct.error as error:
    raise ValueError("el valor no cabe en 16 bits con signo") from error

Valida también valores reservados, combinaciones de flags, NaN, infinito y rangos de negocio.

No es un serializador universal

struct no guarda nombres, evolución de esquema ni tipos semánticos. Es excelente para layouts compactos y estables. JSON o formatos con esquema son mejores para objetos flexibles.

Seguridad

  • Valida la longitud del buffer.
  • Define endianness.
  • Limita longitudes declaradas.
  • Rechaza versiones desconocidas.
  • Usa timeouts.
  • Define bytes reservados y padding.
  • No uses P con datos externos.
  • Haz fuzzing del parser.

Pruebas

Prueba valores mínimos y máximos, endian invertido, buffers cortos, bytes extra, registros incompletos, NaN, infinito, NUL, versiones futuras y payloads superiores al máximo. Verifica bytes exactos, no solo round trips.

def test_header_bytes():
    assert HEADER.pack(b"APP1", 1, 5) == b"APP1\x01\x00\x00\x00\x05"

Buenas prácticas

  • Crea constantes Struct con nombre.
  • Documenta campos y unidades.
  • Incluye magia y versión.
  • Usa tamaños estándar.
  • Valida antes de reservar.
  • Usa pack_into y memoryview.
  • No vuelques binarios sensibles en logs.

Conclusión

struct en Python conecta valores Python con layouts binarios eficientes. Funciona muy bien para cabeceras, archivos y protocolos cuando orden, tamaño, versión y límites son explícitos.

Consulta la documentación oficial de struct y la documentación del protocolo de buffer. Para límites por entorno, revisa configparser en Python, y para seguir la ejecución usa trace en Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile en Python: crea TAR seguro

    Aprende tarfile en Python para crear TAR comprimido, inspeccionar miembros y extraer con filtros, límites y protección de rutas.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Row of colorful office binders neatly arranged on a shelf, ideal for organization concepts.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    gzip en Python: comprime archivos .gz

    Aprende gzip en Python para leer y escribir .gz, crear streams reproducibles, procesar datos grandes y limitar la expansión externa.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Neatly arranged blue office binders labeled with dates and names for organized storage.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    lzma en Python: comprime archivos XZ

    Aprende lzma en Python para crear archivos XZ, procesar streams, elegir checks y filtros y limitar memoria con datos externos.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    bz2 en Python: comprime con bzip2

    Aprende bz2 en Python para comprimir archivos y bytes, procesar streams por bloques y limitar la expansión de datos externos.

    Ler mais

    Tempo de leitura: 4 minutos
    16/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    zlib en Python: comprime datos

    Aprende zlib en Python para comprimir y descomprimir bytes, procesar streams, usar checksums y limitar datos externos.

    Ler mais

    Tempo de leitura: 6 minutos
    16/08/2026
    Diagrama de archivos y sistema que representa configuración INI con configparser en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    configparser en Python: archivos INI

    Aprende configparser en Python para leer y escribir archivos INI, combinar defaults, usar interpolación y actualizar de forma segura.

    Ler mais

    Tempo de leitura: 5 minutos
    16/08/2026