codecs no Python: domine encodings

Publicado em: 18/08/2026
Tempo de leitura: 7 minutos
A person reads 'Python for Unix and Linux System Administration' indoors.

O módulo codecs no Python fornece acesso ao registry de encodings, funções genéricas de codificação e decodificação, classes incrementais, leitores e escritores de stream, marcadores BOM e mecanismos para registrar codecs e tratadores de erro personalizados.

Na maioria dos programas, você não precisa importar codecs para abrir um arquivo UTF-8. O open() embutido e o módulo io são a abordagem recomendada. O módulo se torna importante quando a aplicação precisa inspecionar o registry, processar sequências parciais, criar transcodificação, trabalhar com transformações não textuais ou implementar um codec.

Texto e bytes são tipos diferentes

Uma string Python contém caracteres Unicode. Para gravá-la em arquivo ou enviá-la pela rede, você precisa convertê-la em bytes usando um encoding. Decodificação faz o caminho inverso:

texto = "Olá, Python"
dados = texto.encode("utf-8")
restaurado = dados.decode("utf-8")

assert restaurado == texto

O nome do encoding faz parte do protocolo. Decodificar bytes UTF-8 como Latin-1 pode não gerar erro, mas produzir texto corrompido. Não tente “adivinhar” silenciosamente quando a origem deveria informar a codificação.

codecs.encode e codecs.decode

import codecs

bytes_utf8 = codecs.encode("ação", "utf-8", errors="strict")
texto = codecs.decode(bytes_utf8, "utf-8", errors="strict")

print(bytes_utf8)
print(texto)

Essas funções usam o registry e funcionam também com transformações que não estão disponíveis em str.encode() ou bytes.decode(). Para encodings de texto comuns, os métodos dos próprios objetos são mais diretos.

Consulte um codec no registry

import codecs

info = codecs.lookup("utf-8")
print(info.name)
print(info.encode)
print(info.decode)
print(info.incrementalencoder)
print(info.incrementaldecoder)

lookup() normaliza aliases e devolve um CodecInfo. Um nome desconhecido gera LookupError. Valide o encoding informado em configuração durante a inicialização, não somente quando o primeiro arquivo for processado.

Aliases e nomes normalizados

Nomes como utf-8, UTF_8 e utf8 normalmente apontam para o mesmo codec. Espaços e hífens são normalizados. Ainda assim, use nomes conhecidos e consistentes. Em CPython, alguns aliases comuns possuem caminhos otimizados, enquanto variantes incomuns podem perder essa otimização.

Use open em arquivos de texto

from pathlib import Path

caminho = Path("relatorio.txt")

with caminho.open("w", encoding="utf-8", newline="\n") as arquivo:
    arquivo.write("linha 1\nlinha 2\n")

with caminho.open("r", encoding="utf-8") as arquivo:
    conteudo = arquivo.read()

codecs.open() está depreciado desde o Python 3.14 e foi substituído por open(). Além de ser a API moderna, open() integra buffering, tratamento de newline e a infraestrutura do módulo io.

Migre codecs.open

# Antigo
import codecs
arquivo = codecs.open("dados.txt", "r", encoding="utf-8")

# Atual
arquivo = open("dados.txt", "r", encoding="utf-8")

Revise diferenças de newline: quando codecs.open() recebe um encoding, o arquivo subjacente é aberto em binário e não realiza conversão automática de \n. Teste arquivos produzidos em Windows e Unix durante a migração.

Tratamento de erros

O padrão strict gera UnicodeEncodeError ou UnicodeDecodeError. Esse comportamento é a melhor opção quando perda de dados não é aceitável.

dados = b"nome: Jos\xe9"

try:
    texto = dados.decode("utf-8", errors="strict")
except UnicodeDecodeError as erro:
    print(erro.start, erro.end, erro.reason)

Os principais handlers são:

  • strict: falha imediatamente.
  • replace: usa ao decodificar ou ? ao codificar.
  • ignore: descarta dados inválidos silenciosamente.
  • backslashreplace: preserva a informação como escapes.
  • surrogateescape: permite round trip de bytes inválidos em interfaces do sistema.
  • xmlcharrefreplace: gera referências numéricas ao codificar XML ou HTML.
  • namereplace: usa nomes Unicode em escapes.

Evite errors ignore

errors="ignore" pode remover letras de nomes, separadores, números e caracteres de segurança sem aviso. Isso transforma dados corrompidos em dados aparentemente válidos. Use apenas quando a perda estiver prevista, for mensurada e não alterar significado.

Preserve bytes desconhecidos com surrogateescape

surrogateescape é útil ao manipular nomes do sistema de arquivos que não podem ser decodificados corretamente:

dados = b"arquivo_\xff.txt"
texto = dados.decode("utf-8", errors="surrogateescape")
restaurado = texto.encode("utf-8", errors="surrogateescape")
assert restaurado == dados

O texto intermediário contém code points surrogate e não deve ser enviado indiscriminadamente para JSON, banco ou interface. A finalidade é preservar os bytes para devolvê-los ao mesmo ambiente.

Registre um error handler

import codecs
import logging

logger = logging.getLogger(__name__)

def registrar_e_substituir(erro):
    logger.warning(
        "falha de encoding entre %d e %d",
        erro.start,
        erro.end,
    )
    return ("?", erro.end)

codecs.register_error("registrar_substituir", registrar_e_substituir)
resultado = "preço: €".encode("ascii", errors="registrar_substituir")

Um handler deve sempre avançar a posição; retornar o mesmo índice pode criar loop infinito. Não registre conteúdo sensível completo. Nomeie handlers com prefixo do projeto para evitar colisões.

Encoding incremental

Um caractere UTF-8 pode ser dividido entre blocos. Decodificar cada bloco isoladamente falha ou corrompe o resultado. Use um decoder incremental, que guarda bytes incompletos:

import codecs

decoder = codecs.getincrementaldecoder("utf-8")(errors="strict")
partes = []

for bloco in receber_blocos():
    partes.append(decoder.decode(bloco, final=False))

partes.append(decoder.decode(b"", final=True))
texto = "".join(partes)

A chamada final força o decoder a processar buffers restantes e a gerar erro se a última sequência estiver incompleta.

Encoder incremental

import codecs

encoder = codecs.getincrementalencoder("utf-16")()
saida = bytearray()

for trecho in gerar_texto():
    saida.extend(encoder.encode(trecho, final=False))

saida.extend(encoder.encode("", final=True))

Encodings stateful podem produzir marcadores ou manter estado entre chamadas. Não crie um encoder novo para cada fragmento.

iterencode e iterdecode

import codecs

blocos_texto = ["Olá ", "mundo", "!\n"]
for bloco in codecs.iterencode(blocos_texto, "utf-8"):
    enviar(bloco)

blocos_bytes = [b"Ol\xc3", b"\xa1 mundo"]
texto = "".join(codecs.iterdecode(blocos_bytes, "utf-8"))

iterencode() exige itens str; iterdecode() exige bytes. Nem todo codec de transformação binária ou texto-para-texto é compatível com essas funções.

Processamento de arquivos grandes

Para arquivos textuais, o objeto retornado por open() já usa um decoder incremental. Iterar por linha evita carregar tudo. As técnicas de leitura de arquivos gigantes continuam válidas: blocos, limites, streaming e escrita incremental.

BOM em UTF-16 e UTF-32

UTF-16 e UTF-32 podem usar um Byte Order Mark para indicar endianness. O módulo expõe constantes como BOM_UTF16_LE, BOM_UTF16_BE, BOM_UTF32_LE e BOM_UTF32_BE.

import codecs

dados = codecs.BOM_UTF16_LE + "texto".encode("utf-16-le")
print(dados.startswith(codecs.BOM_UTF16_LE))

Quando você usa o encoding utf-16, Python interpreta ou produz o BOM apropriado. Com utf-16-le e utf-16-be, a ordem é explícita e o BOM não deve ser presumido.

UTF-8 com BOM

UTF-8 não precisa de byte order. Alguns programas usam os bytes EF BB BF como assinatura. O encoding utf-8-sig remove esse prefixo ao ler e o grava ao escrever:

from pathlib import Path

texto = Path("planilha.csv").read_text(encoding="utf-8-sig")

Use utf-8-sig quando interoperar com software que produz BOM. Para novos protocolos, prefira UTF-8 sem BOM e declare a codificação externamente.

Detectar encoding é difícil

Não existe algoritmo perfeito para descobrir o encoding de bytes arbitrários. Qualquer sequência pode ser válida em várias páginas de código. Use metadados, cabeçalhos, contrato de fornecedor ou configuração. Heurísticas devem fornecer confiança, permitir revisão e nunca transformar silenciosamente dados críticos.

Transcodificação

Converter um arquivo Latin-1 para UTF-8 deve decodificar e depois codificar:

from pathlib import Path

origem = Path("legado.txt")
destino = Path("novo.txt")

with origem.open("r", encoding="latin-1", errors="strict") as entrada:
    with destino.open("w", encoding="utf-8", newline="") as saida:
        for linha in entrada:
            saida.write(linha)

Não faça substituição direta de bytes. Para escrita atômica, grave em arquivo temporário e renomeie após sucesso.

Registry de codecs personalizados

codecs.register() adiciona uma função de busca. Ela recebe o nome normalizado e devolve CodecInfo ou None. unregister(), disponível desde o Python 3.10, remove a função e limpa o cache.

Um codec completo precisa definir encoder e decoder stateless e, quando necessário, classes incrementais e de stream. Faça isso apenas quando existe um formato real compartilhado. Uma função comum é mais simples para uma transformação interna.

Transformações binárias

O registry também contém codecs bytes-para-bytes, como base64_codec, hex_codec, bz2_codec e zlib_codec:

import codecs

codificado = codecs.encode(b"dados", "base64_codec")
restaurado = codecs.decode(codificado, "base64_codec")

Para Base64 de aplicação, a API específica de base64 no Python é mais clara e permite validate=True. Para compressão, use diretamente os módulos correspondentes.

Não confunda encoding com layout binário

Um encoding de texto converte caracteres em bytes. Ele não define campos, números ou cabeçalhos. Para um protocolo binário estruturado, use struct no Python e documente endianness, versão e tamanhos.

Configuração de encoding

Se o encoding for configurável, valide-o com codecs.lookup() na inicialização. configparser no Python pode carregar o nome, mas mantenha uma allowlist quando somente alguns encodings são suportados. Evite aceitar qualquer codec Python em entrada externa, pois o registry inclui transformações que não são texto.

Segurança e limites

  • Limite tamanho dos bytes e do texto resultante.
  • Use strict para dados críticos.
  • Evite detecção automática sem metadados.
  • Não exponha surrogates em formatos externos.
  • Finalize decoders incrementais.
  • Não registre dados sensíveis em erros.
  • Use allowlist de encodings.
  • Teste consumo de CPU em codecs como IDNA e Punycode com entrada não confiável.

Testes recomendados

Teste ASCII, acentos, emoji, caracteres fora do encoding, sequências UTF-8 partidas entre blocos, último bloco incompleto, BOM correto e invertido, UTF-8 com assinatura, arquivos vazios, newlines de Windows e Unix, handlers de erro e aliases. Inclua round trip e bytes esperados.

Boas práticas

  • Use UTF-8 como padrão de novos formatos.
  • Declare o encoding no protocolo.
  • Use open() no lugar de codecs.open().
  • Prefira strict e trate falhas.
  • Use decoder incremental para chunks manuais.
  • Valide nomes no registry.
  • Mantenha texto como str e transporte como bytes.
  • Faça transcodificação em streaming.

Conclusão

O codecs no Python é a infraestrutura que conecta nomes de encodings, funções, streams, handlers e processamento incremental. Ele é especialmente útil em interoperabilidade, protocolos e bibliotecas; para arquivos comuns, open() continua sendo a interface preferida.

Consulte a documentação oficial do codecs e o Unicode Standard. O encoding correto deve ser definido pelo contrato, não descoberto por tentativa e erro.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A person reads 'Python for Unix and Linux System Administration' indoors.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    base64 no Python: codifique dados

    Aprenda base64 no Python para codificar bytes, usar Base64 URL-safe, validar padding, aplicar limites e diferenciar encoding de criptografia.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    urllib.parse no Python: manipule URLs

    Aprenda urllib.parse no Python para decompor URLs, criar queries, codificar componentes e evitar riscos com urljoin, redirects e SSRF.

    Ler mais

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

    ipaddress no Python: redes IPv4 e IPv6

    Aprenda ipaddress no Python para validar IPv4 e IPv6, calcular redes CIDR, dividir sub-redes e criar políticas de acesso seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026
    A vibrant collection of blue sewing threads arranged with hands on a white background.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    queue no Python: coordene threads

    Aprenda queue no Python para coordenar threads com FIFO, prioridade, backpressure, task_done, join, retries e shutdown seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    17/08/2026
    A male software engineer working on code in a modern office setting.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    struct no Python: trabalhe com binário

    Aprenda struct no Python para empacotar dados binários, controlar endianness, usar buffers e validar protocolos e arquivos externos.

    Ler mais

    Tempo de leitura: 5 minutos
    17/08/2026
    Close-up of a laptop screen with code and a coffee mug, perfect for tech abstract themes.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile no Python: crie TAR seguro

    Aprenda tarfile no Python para criar TAR comprimido, inspecionar membros e extrair com filtros, limites e proteção contra path traversal.

    Ler mais

    Tempo de leitura: 5 minutos
    17/08/2026