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

    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026