struct en Python: datos binarios

Publicado el: 28/08/2026
Tempo de leitura: 5 minutos
Close-up of a man with binary code projected on his face, symbolizing cybersecurity.

El módulo struct convierte valores Python en secuencias de bytes y reconstruye valores desde buffers binarios. Se utiliza en protocolos de red, headers de archivos, dispositivos, bases binarias, memoria compartida e integración con programas C. Una string de formato describe tipos, tamaños, alineación y orden de bytes.

Usar struct exige precisión. Un formato incorrecto puede interpretar bytes válidos como valores absurdos, truncar información o provocar asignaciones excesivas. Define un layout documentado, valida tamaños antes de desempaquetar y nunca confíes en contadores u offsets leídos de archivos o conexiones.

Primer pack y unpack

pack() recibe un formato y valores; unpack() realiza la conversión inversa.

import struct

datos = struct.pack("!HI", 2, 4096)
version, longitud = struct.unpack("!HI", datos)
print(version, longitud)

El prefijo ! selecciona orden de red, equivalente a big-endian con tamaños estandarizados.

Caracteres de formato

Los códigos frecuentes incluyen b y B para enteros de 8 bits, h/H para 16 bits, i/I para enteros, q/Q para 64 bits, f y d para floating point, ? para booleanos, s para bytes de tamaño fijo y x para padding.

Documenta el significado de cada campo fuera de la string. Un layout compacto sin nombres es difícil de revisar.

Endianness

Los prefijos controlan orden y alineación: > es big-endian, < little-endian, ! orden de red, = orden nativo con tamaños estándar y @ layout nativo completo.

little = struct.pack("<I", 0x12345678)
big = struct.pack(">I", 0x12345678)
print(little.hex(), big.hex())

Los formatos persistentes deben elegir un orden explícito. No uses @ para datos intercambiados entre arquitecturas.

Calcula el tamaño

calcsize() informa cuántos bytes ocupa un formato.

FORMATO = "!4sBHI"
TAMANO = struct.calcsize(FORMATO)

Usa este valor para validar buffers y calcular offsets sin números mágicos.

Bytes de tamaño fijo

El especificador Ns representa exactamente N bytes.

header = struct.pack("!4sI", b"DATA", 10)
magic, longitud = struct.unpack("!4sI", header)

Una entrada corta se completa con ceros y una entrada larga se trunca. Valida la longitud cuando el truncamiento silencioso sea peligroso.

Texto y encoding

struct no codifica strings Python. Convierte texto explícitamente.

nombre = "acción".encode("utf-8")
if len(nombre) > 32:
    raise ValueError("nombre demasiado largo")
bloque = struct.pack("!32s", nombre)

Al decodificar, elimina padding únicamente según el protocolo.

unpack_from

unpack_from() lee campos desde un buffer existente a partir de un offset sin crear un slice.

version, flags = struct.unpack_from("!BB", buffer, 4)

Comprueba que offset + calcsize(formato) quepa en el buffer.

pack_into

pack_into() escribe en un buffer mutable como bytearray, memoryview o mmap.

buffer = bytearray(16)
struct.pack_into("!I", buffer, 0, 123)

Esto reduce asignaciones en loops, pero exige control riguroso de offsets y concurrencia.

iter_unpack

iter_unpack() recorre registros de tamaño fijo.

FORMATO = "!Ih"
for identificador, valor in struct.iter_unpack(FORMATO, datos):
    procesar(identificador, valor)

La longitud total debe ser múltiplo del tamaño del registro.

Registros variables

Para payloads variables, utiliza un header fijo que declare la longitud del cuerpo.

HEADER = "!I"
longitud = struct.unpack(HEADER, recibir_exacto(4))[0]
if longitud > 1_000_000:
    raise ValueError("payload demasiado grande")
payload = recibir_exacto(longitud)

Aplica el límite antes de reservar memoria.

Enteros con signo

Los códigos minúsculos suelen representar enteros con signo y los mayúsculos, sin signo. Valores fuera del rango generan struct.error.

También valida el dominio de aplicación. Un valor representable puede ser inválido para el protocolo.

Punto flotante

f representa precisión simple y d, doble. Los resultados pueden contener redondeo, infinito y NaN.

No uses floats binarios para dinero o contadores exactos. Prefiere enteros escalados o una representación decimal definida.

Booleanos

El formato ? serializa booleanos. Al desempaquetar, cualquier byte distinto de cero se interpreta como verdadero.

Si el protocolo permite únicamente 0 o 1, valida el byte o usa B.

Padding y alineación

El modo nativo @ puede insertar padding para reproducir una estructura C local. El layout cambia según arquitectura y ABI.

Para archivos y red, prefiere <, > o !. Usa layout nativo solo para integración local probada en cada plataforma.

Integración con mmap

unpack_from() puede leer directamente de un archivo mapeado.

import mmap

with mmap.mmap(archivo.fileno(), 0, access=mmap.ACCESS_READ) as mapa:
    magic, version = struct.unpack_from("!4sH", mapa, 0)

Consulta mmap en Python para lifecycle, alineación y sincronización.

Integración con sockets

TCP es un flujo; una llamada a recv() puede devolver menos bytes de los necesarios. Recibe exactamente el header antes de desempaquetar.

Consulta socket en Python para framing y timeouts.

Clase Struct

Cuando reutilices un formato, compílalo con struct.Struct.

HEADER = struct.Struct("!4sBHI")
bloque = HEADER.pack(b"DATA", 1, 0, 128)
campos = HEADER.unpack(bloque)

Esto centraliza el layout y puede reducir overhead.

Layouts versionados

Incluye magic bytes y versión al principio. El parser puede seleccionar un layout conocido y rechazar versiones desconocidas.

Añade campos de forma compatible o crea una versión nueva. No cambies silenciosamente el significado de bytes existentes.

Checksums y autenticidad

Un checksum detecta corrupción accidental, pero no demuestra autenticidad frente a un atacante.

Usa MAC o firma digital cuando necesites origen e integridad, cubriendo header y payload.

Offsets y overflow lógico

Los enteros Python son grandes, pero la aritmética de offsets puede superar el buffer o solicitar asignaciones enormes.

fin = offset + cantidad * tamano_item
if cantidad > LIMITE or fin > len(buffer):
    raise ValueError("estructura inválida")

Valida antes de crear slices o colecciones.

Datos no confiables

unpack() no ejecuta código, pero un parser inseguro puede agotar CPU o memoria.

Limita profundidad, contadores, longitudes y tiempo. Usa aislamiento de proceso cuando el riesgo lo justifique.

Excepciones

Errores de formato, tamaño o rango generan struct.error.

try:
    campos = struct.unpack(FORMATO, bloque)
except struct.error as error:
    raise ValueError("registro binario inválido") from error

No registres payloads binarios completos.

Pruebas

Prueba valores mínimos y máximos, cero, negativos, ambas órdenes, NaN, padding, buffers cortos, datos extra, versiones desconocidas y archivos truncados.

Usa vectores conocidos generados por otro lenguaje para comprobar interoperabilidad.

Errores comunes

Los fallos frecuentes son usar layout nativo en archivos portables, olvidar calcsize(), truncar strings silenciosamente, desempaquetar tamaños incorrectos, confiar en longitudes externas, usar float para valores exactos y asumir que un recv entrega un registro completo.

Conclusión

struct es el puente entre valores Python y layouts binarios compactos. Elige endianness explícita, centraliza formatos, valida buffers y versiona protocolos persistentes.

Consulta la documentación oficial de struct, mmap en Python y socket en Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Close-up of hands using a compact black calculator on a white marble surface, displaying numbers.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    array en Python: números compactos

    Aprende array en Python para almacenar números compactos, usar typecodes, bytes, archivos, memoryview y layouts binarios portables.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A young girl exploring a library's card catalog, symbolizes research and curiosity.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    mmap en Python: archivos en memoria

    Aprende mmap en Python para mapear archivos, buscar bytes, editar regiones, compartir memoria, alinear offsets y sincronizar accesos.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Close-up of a hand pointing at audio editing software on a monitor in a recording studio.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    importlib.metadata: versiones y plugins

    Aprende importlib.metadata en Python para consultar versiones, requisitos, archivos, distribuciones, entry points y plugins sin importar paquetes.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026
    A public library bookshelf displaying a variety of books and DVDs, providing a cozy reading atmosphere.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    importlib.resources: lee archivos de paquetes

    Aprende importlib.resources en Python para leer templates y datos con Traversable, files y as_file en wheels, ZIPs y aplicaciones frozen.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    runpy en Python: ejecuta módulos y scripts

    Aprende runpy en Python para ejecutar módulos y scripts, controlar __main__, run_path, alter_sys, namespaces, tests y aislamiento.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Close-up of a python snake coiled in darkness, showcasing its scales and eyes.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pkgutil en Python: descubre paquetes

    Aprende pkgutil en Python para listar módulos, recorrer paquetes, descubrir plugins, consultar importers y leer recursos con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026