Como importar CSV sem corromper caracteres: encoding, BOM e delimitadores
UTF-8, latin1, cp1252, BOM — por que seus acentos aparecem como lixo e como corrigir de uma vez, no Excel, Python e linha de comando.
Todo pipeline de dados tem aquela fase clássica: você recebe um CSV de alguém, abre no editor, e metade dos campos aparece como ção ou “nomeâ€. A exportação estava certa. O arquivo chegou correto. Só que o encoding não bate, e agora você tem lixo onde deveria ter "São Paulo" ou "descrição".
Esse problema não é raro — é o padrão quando times de dados, sistemas legados e Excel convivem no mesmo pipeline. Entender o que está acontecendo leva quinze minutos. Resolver sem entender pode levar dias.
O que é encoding e por que CSV não resolve isso pra você
CSV é um formato textual sem cabeçalho de metadados. Diferente de JSON (que também é texto, mas tem especificação de encoding implícita em UTF-8) ou de XLSX (que é um ZIP com XML que declara o encoding), um arquivo .csv não carrega informação sobre como foi codificado. Você simplesmente abre e torce para o encoding bater.
Os encodings mais comuns no Brasil:
- UTF-8 — padrão da web, do Linux, do macOS, de praticamente toda stack moderna
- ISO-8859-1 (latin1) — legado europeu, cobre acentos do português
- Windows-1252 (cp1252) — superset do latin1, usado pelo Windows em BR; é o que o Excel gera por padrão quando você "Salvar Como CSV" em PT-BR
A diferença prática: ç em UTF-8 é dois bytes (0xC3 0xA7). Em latin1 e cp1252, é um byte (0xE7). Quando você abre um arquivo cp1252 como se fosse UTF-8, esses dois bytes são interpretados como dois caracteres separados — e você obtém ç.
Excel quebrando acentos: o problema mais comum
O Excel tem um comportamento bem documentado e bem irritante: ao abrir um CSV com duplo-clique no Windows, ele assume cp1252 (no Brasil) e não pergunta nada. Se o arquivo estiver em UTF-8, tudo que tem acento vira lixo.
A solução correta não é converter o arquivo — é usar o assistente de importação:
- No Excel, vá em Dados → Obter Dados → De Arquivo de Texto/CSV
- Selecione o arquivo
- Na tela de preview, mude Origem do Arquivo para
65001: Unicode (UTF-8) - Ajuste o delimitador se necessário
- Carregue
Versões mais antigas do Excel usam o caminho Dados → Obter Dados Externos → De Texto com o mesmo passo de seleção de codificação no assistente. O resultado é idêntico.
Se o arquivo realmente estiver em cp1252 (comum em exportações de sistemas legados, ERPs antigos, planilhas geradas pelo Windows), selecione a opção correspondente — geralmente 1252: Europa Ocidental (Windows).
UTF-8 com BOM: a gambeta que pode ajudar ou atrapalhar
BOM (Byte Order Mark) é uma sequência de três bytes no início do arquivo (0xEF 0xBB 0xBF) que sinaliza explicitamente que o arquivo está em UTF-8. Não é obrigatório pela spec, mas alguns programas — incluindo o Excel — usam o BOM para detectar automaticamente o encoding ao abrir por duplo-clique.
Se você controla a geração do CSV e sabe que vai ser aberto no Excel, adicionar BOM é uma forma pragmática de evitar o problema:
# Python: escrever CSV com BOM para compatibilidade com Excel
import csv
with open('saida.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerow(['nome', 'cidade', 'descrição'])
writer.writerow(['João', 'São Paulo', 'cliente ativo'])
O encoding utf-8-sig no Python escreve o BOM automaticamente. O arquivo continua sendo UTF-8 válido — a diferença é que o Excel vai detectar corretamente.
O lado ruim do BOM: alguns parsers antigos ou mal escritos tratam os três bytes como parte do conteúdo e você vê nome na primeira coluna. Ferramentas modernas (Python csv, pandas, PostgreSQL COPY) ignoram o BOM corretamente ou têm opção para isso.
Diagnosticando o encoding de um arquivo desconhecido
Antes de tentar converter, descubra o que você tem:
# Linux/macOS — file detecta encoding por heurística
file -i dados.csv
# saída esperada: dados.csv: text/plain; charset=utf-8
# Python — chardet faz detecção estatística
python -c "
import chardet
with open('dados.csv', 'rb') as f:
print(chardet.detect(f.read(100_000)))
"
# {'encoding': 'ISO-8859-1', 'confidence': 0.73, 'language': ''}
A confiança abaixo de 0.9 é sinal de arquivo misto ou encoding incomum. Nesses casos, abra o arquivo em modo binário e inspecione os bytes manualmente — ou tente cada encoding candidato e veja qual produz texto legível.
Convertendo encoding na linha de comando
Quando o encoding está confirmado, a conversão é trivial:
# iconv: ferramenta padrão Unix
iconv -f windows-1252 -t utf-8 entrada.csv > saida.csv
# Python: mais flexível, lida com erros
python -c "
with open('entrada.csv', 'r', encoding='cp1252') as f_in, \
open('saida.csv', 'w', encoding='utf-8') as f_out:
f_out.write(f_in.read())
"
Para pipelines recorrentes, normalizar para UTF-8 na entrada é a decisão certa — uma vez só, antes de qualquer processamento. Isso elimina a classe inteira de problemas downstream.
Delimitadores: o problema que vem junto
Encoding e delimitador costumam aparecer juntos como fonte de corrupção. CSV não é um formato padronizado de verdade: vírgula, ponto-e-vírgula e tab são todos usados dependendo da região e do sistema de origem.
Sistemas europeus e brasileiros frequentemente usam ponto-e-vírgula como delimitador porque a vírgula é o separador decimal local. Um CSV com ; como delimitador que você tenta parsear com , vai parecer ter uma única coluna gigante com todos os campos concatenados.
# pandas: sempre especifique sep e encoding explicitamente
import pandas as pd
df = pd.read_csv(
'dados.csv',
sep=';', # não confie no sniff automático para produção
encoding='cp1252', # ou 'utf-8', dependendo da origem
dtype=str # evite conversão automática de tipos na leitura
)
O dtype=str merece destaque: pandas converte CPFs, CEPs e códigos com zeros à esquerda para inteiro por padrão. 01310-100 vira 1310100. Leia como string, valide depois.
Se você trabalha com ETL e está integrando diferentes fontes de CSV numa pipeline maior, o contexto de ETL vs ELT ajuda a entender onde colocar essa normalização de encoding — no extract ou no transform.
Inspecionando e convertendo arquivos no browser
Para arquivos pequenos ou inspeção rápida sem instalar nada, uso o CSV para JSON do Quick Tools para visualizar o conteúdo e verificar se os acentos estão chegando corretamente antes de processar. Se o arquivo abre com lixo no preview, o problema está no encoding da origem — e eu já sei onde corrigir antes de botar no pipeline.
Perguntas frequentes
Por que meu CSV abre errado no Excel mas está correto em outros programas?
O Excel no Windows assume cp1252 ao abrir CSVs por duplo-clique. Se o arquivo estiver em UTF-8, acentos e caracteres especiais aparecem corrompidos. A solução é importar pelo menu Dados → Obter Dados, não abrir diretamente. Lá você escolhe o encoding correto antes de carregar.
Como verificar se um arquivo CSV está em UTF-8 ou latin1?
Use file -i arquivo.csv no terminal (Linux/macOS) ou o módulo chardet no Python. Para Windows, o Notepad++ mostra o encoding na barra inferior e permite recodificar via menu Codificação. Um sinal visual rápido: se o arquivo tiver acentos e você os ver como ção, o arquivo provavelmente está em UTF-8 sendo lido como latin1. Se vir ????, pode ser qualquer encoding não suportado.
Devo sempre usar UTF-8 nos meus CSVs?
Sim, para qualquer sistema novo. UTF-8 é o encoding padrão da web, de todas as linguagens modernas e da maioria dos bancos de dados. Se você precisa de compatibilidade com Excel sem que o usuário configure nada, use utf-8-sig (UTF-8 com BOM) — o Excel detecta automaticamente e abre corretamente.
O que fazer quando o CSV tem encoding misto, com algumas linhas em UTF-8 e outras em latin1?
Isso acontece quando arquivos de fontes diferentes são concatenados sem normalização. A abordagem mais robusta é ler linha por linha, tentar UTF-8 primeiro e, em caso de erro de decodificação, tentar latin1/cp1252:
def decode_line(raw_bytes):
try:
return raw_bytes.decode('utf-8')
except UnicodeDecodeError:
return raw_bytes.decode('cp1252', errors='replace')
O errors='replace' substitui bytes indecodificáveis por ? em vez de lançar exceção — útil para não travar o pipeline em dados sujos.
Encoding correto não é opcional
A maioria dos bugs de encoding em pipelines de dados não é difícil de resolver — é difícil de diagnosticar porque o sintoma (dado corrompido) aparece longe da causa (arquivo aberto com encoding errado). Normalizar para UTF-8 na entrada do pipeline, especificar encoding e delimitador explicitamente no código e nunca confiar no sniff automático são três regras que eliminam essa categoria de problema de vez.
- 01 Como os LLMs geram respostas: tokens, predição e sampling explicados Tokenização, predição autorregressiva, temperatura e Top-P: a mecânica interna de como modelos de linguagem transformam um prompt em texto.
- 02 Clean Code sem dogmas: o que realmente importa Clean Code virou religião — e tem fiéis que aplicam os mandamentos sem entender a teologia. O que realmente reduz bugs e custo de manutenção.