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 == textoO 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 == dadosO 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
strictpara 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 decodecs.open(). - Prefira
stricte trate falhas. - Use decoder incremental para chunks manuais.
- Valide nomes no registry.
- Mantenha texto como
stre 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.







