Erros de Codificação e Unicode
Erros de texto aparecem quando bytes são decodificados com o codec errado, str e bytes se misturam sem um limite explícito, ou strings Unicode visualmente idênticas comparam como desiguais devido à falta de normalização.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
# Sempre rotule bytes; decodifique no limite
raw: bytes = b"\xc3\xa9" # UTF-8 para é
text: str = raw.decode("utf-8")
# Escrevendo texto
path.write_text(text, encoding="utf-8")
path.write_bytes(raw)Quando usar isto:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff- Concatenar
str+byteslevantaTypeError - Pesquisa do usuário não encontra correspondências para caracteres acentuados
- Logs mostram
mojibakecomoéem vez deé
Exemplo de Trabalho
import unicodedata
from pathlib import Path
# --- bug: decodificação implícita ---
def read_config_legacy(path: str) -> str:
with open(path) as f: # modo texto sem encoding= em casos extremos de Windows/locale
return f.read()
# --- correção: UTF-8 explícito ---
def read_config(path: str) -> str:
return Path(path).read_text(encoding="utf-8")
# --- bug: misturando str e bytes ---
def broken_join(prefix: str, payload: bytes) -> bytes:
return prefix + payload # TypeError
# --- correção: codificar no limite ---
def fixed_join(prefix: str, payload: bytes) -> bytes:
return prefix.encode("utf-8") + payload
# --- bug: comparação sem normalização ---
def usernames_equal(a: str, b: str) -> bool:
return a == b
# --- correção: normalização NFC para identificadores de usuário ---
def usernames_equal_normalized(a: str, b: str) -> bool:
return unicodedata.normalize("NFC", a.casefold()) == unicodedata.normalize("NFC", b.casefold())
# --- tratamento seguro de bytes desconhecidos ---
def decode_best_effort(raw: bytes) -> str:
return raw.decode("utf-8", errors="replace")
# demo
assert decode_best_effort(b"ok\xc3\xa9") == "oké"
assert usernames_equal_normalized("café", "café") # formas visualmente iguaisO que isto demonstra:
- Arquivos de texto abertos com
encoding="utf-8"explícito - Codificar
strparabytesantes da concatenação binária unicodedata.normalizemaiscasefoldpara igualdade de strings robustaerrors="replace"para registrar dados corrompidos sem falhar
Mergulho Profundo
Como Funciona
strsão pontos de código Unicode;bytessão octetos brutos- Codificações (UTF-8, Latin-1) mapeiam bytes ↔ str em limites de IO
- O código fonte Python 3 é UTF-8 por padrão (PEP 3120)
- Formas de normalização (NFC, NFD) alinham sequências canonicamente equivalentes
Pontos Comuns de Falha
| Localização | Erro | Correção |
|---|---|---|
open() | codificação de locale padrão | encoding="utf-8" |
| HTTP | charset Content-Type incorreto | decodificar por cabeçalho |
| JSON | json.dumps para bytes manualmente | usar JSON str, codificar uma vez |
| Nomes de arquivo | caminhos de bytes não decodificados | os.fsdecode / pathlib |
| DB | colunas Latin-1 legadas | migrar para UTF-8 ou decodificar explicitamente |
Notas Python
# subprocess: modo texto com codificação explícita (3.14)
import subprocess
subprocess.run(["echo", "hi"], capture_output=True, text=True, encoding="utf-8")
# sqlite armazena TEXT como str Unicode ao usar corretamente a fábrica de textoArmadilhas
- Codificação dupla - Bytes UTF-8 decodificados como Latin-1 e depois salvos como UTF-8 produzem mojibake. Correção: identificar os bytes originais; re-decodificar da fonte.
- Adivinhação
chardetem produção - Palpite errado corrompe dados silenciosamente. Correção: corrigir a codificação upstream; usarerrors="replace"na ingestão. - Normalização de senhas - Altera a entrada do usuário. Correção: normalizar identificadores/exibição, não segredos.
- Comparação de resumos de hash de strings sem codificação -
hash(s)não é para criptografia;hashlibprecisa de.encode("utf-8"). - Emojis e índices de banco de dados legados - Limites de comprimento de colação quebram o armazenamento. Correção: UTF-8 MB4 em configuração MySQL/Postgres.
- Leitura de arquivos binários como texto - PDF/PNG abertos em modo texto. Correção: modo binário + análise do formato.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| UTF-8 em todo lugar | serviços modernos | codificações legadas obrigatórias |
errors="replace" | ingestão de logs | registros brutos financeiros/legais |
| IDNA/punycode | nomes de domínio | armazenamento geral de texto |
bytes ponta a ponta | trabalho de criptografia/protocolo | texto voltado para o usuário sem plano de decodificação |
FAQs
Devo usar utf-8-sig?
Use para arquivos Excel/Windows com BOM. A maioria das APIs e JSON usam UTF-8 simples sem BOM.
Qual a diferença entre NFC e NFD?
NFC compõe caracteres (é como um ponto de código); NFD decompõe (e + acento combinatório). Escolha uma forma no armazenamento e normalize na entrada.
Como corrijo dados de banco de dados Latin-1?
Identifique colunas, migre o esquema para UTF-8, converta bytes com a codificação de origem conhecida em uma janela de manutenção, verifique com amostras de checksum.
O requests lida com codificação automaticamente?
response.text adivinha dos cabeçalhos; defina response.encoding ou use response.content.decode("utf-8") explicitamente quando os cabeçalhos mentirem.
Por que casefold em vez de lower()?
casefold lida com mais casos especiais de Unicode (por exemplo, ß alemão) para comparação insensível a maiúsculas/minúsculas de strings visíveis ao usuário.
Regexes entendem Unicode?
Use o padrão re.UNICODE no Python 3; adicione (?u) ou classes de caracteres como \w sabendo que elas correspondem a letras Unicode.
Como os caminhos pathlib lidam com Unicode?
Path usa str no Unix e APIs Unicode no Windows. Evite passar bytes não decodificados para open no Windows.
E sobre base64 e texto?
Base64 codifica bytes. Decodifique base64 para bytes, depois decodifique bytes para str com o codec de texto correto.
O JSON pode conter não-UTF-8?
JSON é Unicode; json.dumps retorna str. Codifique para bytes UTF-8 apenas no limite de rede/armazenamento.
Como depurar mojibake?
Inspecione bytes brutos com repr(raw) e tente a decodificação correta. Compare o hex UTF-8 esperado (por exemplo, c3 a9 para é).
Python 3.14 muda os padrões de codificação?
O modo UTF-8 (PYTHONUTF8=1) e encoding= explícito continuam sendo as melhores práticas em todas as versões suportadas, incluindo 3.14.0.
Quando errors=ignore é aceitável?
Raramente. Aceitável para exibição com perdas de logs de terceiros, nunca para dados que você persiste ou fatura.
Relacionados
- Migrando um Aplicativo Legado Python 2/Django/Flask - Migração de unicode Py2
- Erros de Ponto Flutuante e Numéricos - classe diferente de bugs de dados
- Defeitos em Pipelines de Dados - codificação CSV em ETL
- Snippets de Refatoração para Depuração - correções de bytes/str
- Ferramentas de Depuração - inspecionar bytes brutos em pdb
Versões de Stack: Esta página foi escrita para Python 3.14.0 (estável 3.14, manutenção 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, e uv 0.6+.