struct no Python: trabalhe com binário

Publicado em: 17/08/2026
Tempo de leitura: 5 minutos
A male software engineer working on code in a modern office setting.

O módulo struct no Python converte valores como inteiros, floats, booleanos e sequências de bytes para layouts binários compactos. O caminho inverso também é suportado: você recebe bytes de um arquivo, dispositivo ou conexão de rede e os interpreta conforme uma estrutura definida por uma string de formato.

Essa ferramenta é útil em protocolos, cabeçalhos de arquivos, integração com C, sensores e formatos legados. O maior risco é confiar implicitamente na plataforma. Sem prefixo, struct usa ordem de bytes, tamanhos e alinhamento nativos, que podem variar. Para intercâmbio externo, defina endianness e tamanhos explicitamente.

Primeiro pack e unpack

import struct

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

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

> seleciona big-endian e tamanhos padronizados. I representa um inteiro sem sinal de 32 bits e h um inteiro com sinal de 16 bits. O resultado de unpack() é sempre uma tupla.

Endianness

A ordem de bytes determina como os bytes mais e menos significativos são armazenados:

  • <: little-endian, tamanhos padrão e sem alinhamento automático.
  • >: big-endian, tamanhos padrão.
  • !: ordem de rede, equivalente a big-endian.
  • =: ordem nativa com tamanhos padrão e sem padding.
  • @: ordem, tamanho e alinhamento nativos.
import struct

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

Em protocolos e arquivos, evite o modo nativo. Defina <, > ou ! para que o layout seja igual em todas as máquinas.

Calcule o tamanho antes de ler

calcsize() informa quantos bytes o formato exige:

import struct

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

with open("mensagem.bin", "rb") as arquivo:
    bruto = arquivo.read(CABECALHO.size)
    if len(bruto) != CABECALHO.size:
        raise ValueError("cabeçalho incompleto")
    magia, versao, flags, tipo, tamanho = CABECALHO.unpack(bruto)

Valide comprimento antes de desempacotar. unpack() exige tamanho exato; unpack_from() precisa de pelo menos o tamanho a partir do offset.

Use Struct para formatos repetidos

A classe Struct compila a string de formato uma vez e reúne tamanho e métodos:

import struct

REGISTRO = struct.Struct("<Iff?")

payload = REGISTRO.pack(42, 18.5, 70.25, True)
identificador, x, y, ativo = REGISTRO.unpack(payload)

Os formatos recentes também são armazenados em cache pelas funções do módulo, mas um objeto nomeado documenta o protocolo e reduz repetição.

Caracteres mais usados

  • b/B: inteiro de 8 bits com/sem sinal.
  • h/H: inteiro de 16 bits.
  • i/I: inteiro padrão de 32 bits.
  • q/Q: inteiro de 64 bits.
  • e, f, d: float de 16, 32 e 64 bits.
  • ?: booleano.
  • s: sequência de bytes de tamanho fixo.
  • x: byte de padding.

No Python 3.14, F e D adicionam números complexos de precisão simples e dupla.

Strings de tamanho fixo

import struct

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

10s é um único campo de dez bytes. Dados maiores são truncados; menores recebem zeros. Valide o comprimento antes de empacotar para não perder conteúdo silenciosamente.

pack_into evita alocação extra

Quando você já possui um bytearray, escreva diretamente:

import struct

buffer = bytearray(1024)
CABECALHO = struct.Struct("!IHH")
CABECALHO.pack_into(buffer, 0, 900, 2, 7)

identificador, versao, flags = CABECALHO.unpack_from(buffer, 0)

As funções aceitam objetos do protocolo de buffer, como bytes, bytearray e memoryview. Isso ajuda a reduzir cópias em pipelines binários.

Leia registros repetidos

iter_unpack() interpreta blocos consecutivos do mesmo tamanho:

import struct

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

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

O comprimento total deve ser múltiplo do tamanho do registro. Rejeite o bloco se houver sobra, pois ela pode indicar corrupção ou versão diferente.

Protocolo com tamanho de payload

Um cabeçalho frequentemente inclui o comprimento do corpo:

import struct

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

header = receber_exatamente(HEADER.size)
magic, version, length = HEADER.unpack(header)

if magic != b"APP1" or version != 1:
    raise ValueError("protocolo inválido")
if length > MAX_PAYLOAD:
    raise ValueError("payload excedeu o limite")

payload = receber_exatamente(length)

Nunca aloque memória apenas com base em um tamanho fornecido pelo remetente. Aplique teto, timeout e limite de mensagens.

Integração com rede

Use ! para ordem de rede. O artigo sobre zlib no Python mostra como um corpo pode ser comprimido, mas o cabeçalho deve informar versão, flags, algoritmo e tamanho de forma inequívoca. O guia de opcode no Python reforça que números internos da VM não são um protocolo estável; defina seus próprios códigos versionados.

Alinhamento nativo

No modo @, o compilador C da plataforma pode inserir padding entre campos. Isso é útil para espelhar uma struct C dentro do mesmo ambiente, mas perigoso para arquivos portáteis.

import struct

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

A ordem dos campos pode mudar o tamanho no modo nativo. Em formato padrão, padding só aparece quando você o declara com x.

Faixas e struct.error

Valores fora da faixa lançam struct.error:

import struct

try:
    struct.pack("!h", 100_000)
except struct.error as erro:
    raise ValueError("valor fora de 16 bits") from erro

Valide também NaN, infinito, valores reservados e combinações de flags conforme o protocolo.

Não use struct como serializador geral

struct não guarda nomes de campos, versões ou esquema. É ideal para layouts compactos e estáveis, não para objetos arbitrários. JSON é melhor para interoperabilidade legível; bancos e formatos como Protocol Buffers oferecem evolução de esquema.

Segurança

  • Valide tamanho antes de desempacotar.
  • Defina endianness explicitamente.
  • Imponha limites a comprimentos declarados.
  • Rejeite versões e códigos desconhecidos.
  • Use timeouts em rede.
  • Não interprete padding ou bytes reservados sem regra.
  • Não use ponteiros P em dados externos.
  • Faça fuzzing do parser.

Testes recomendados

Teste limites mínimos e máximos, endian invertido, buffers curtos, bytes extras, registros incompletos, NaN, infinito, caracteres nulos, versões futuras e payloads acima do teto. Compare os bytes esperados, não apenas o round trip.

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

Boas práticas

  • Crie constantes Struct nomeadas.
  • Documente cada campo e unidade.
  • Inclua magia e versão.
  • Use tamanhos padrão.
  • Valide antes de alocar.
  • Use pack_into e memoryview quando cópias importarem.
  • Registre erros sem despejar dados sensíveis.

Conclusão

O struct no Python é uma ponte eficiente entre valores Python e layouts binários. Ele funciona muito bem em cabeçalhos, arquivos e protocolos, desde que ordem de bytes, tamanhos, versões e limites sejam explícitos.

Consulte a documentação oficial do struct e a documentação do protocolo de buffer. Para parametrizar versões e limites, veja configparser no Python, e para observar o parser em execução use trace no Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Código Python em tela representando EnumDict e enumerações avançadas
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    EnumDict: personalize Enums com validação

    Aprenda EnumDict no Python para personalizar EnumType, validar membros e criar enumerações avançadas com segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    11/10/2026
    Rack de servidores representando balanceamento de conexões com SO_REUSEPORT_LB no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    SO_REUSEPORT_LB: distribua conexões entre workers

    Aprenda SO_REUSEPORT_LB no Python para distribuir conexões entre múltiplos workers com segurança, testes e portabilidade.

    Ler mais

    Tempo de leitura: 6 minutos
    11/10/2026
    Código Python assíncrono em notebook para inspect.markcoroutinefunction
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: identifique wrappers async

    Aprenda inspect.markcoroutinefunction no Python para identificar wrappers assíncronos, integrar frameworks e evitar detecção incorreta de corrotinas.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Código Python para percorrer pastas e arquivos com Path.walk
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: percorra diretórios com segurança

    Aprenda Path.walk no Python para percorrer diretórios, filtrar arquivos, tratar erros e controlar a travessia com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Depuração de processo Python em terminal com código
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: depure processos Python em execução

    Aprenda a anexar o pdb a um processo Python em execução, inspecionar pilhas e diagnosticar travamentos com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Código Python e representação de frações numéricas
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: converta números em frações

    Aprenda fractions.from_number no Python para converter números em frações exatas, controlar precisão e evitar arredondamentos inesperados.

    Ler mais

    Tempo de leitura: 5 minutos
    09/10/2026