Quando o separador vira problema
Você abre um arquivo CSV no Excel e ele mostra uma coluna só porque o sistema não entende que as vírgulas estão dentro de aspas. Isso acontece o tempo todo. A maioria das pessoas acha que é bug do arquivo, mas na verdade é só falta de tratamento correto na hora da leitura. O problema se agrava quando você trabalha com dados internacionais ou exportações de sistemas que misturam formatos sem aviso prévio.
O que é o tanto quanto tem virgula
A expressão "tanto quanto tem vírgula" virou gíria técnica entre desenvolvedores brasileiros de dados. Ela descreve o momento em que você precisa tratar strings que contêm vírgulas como parte do conteúdo e não como separadores de campo. Basicamente, é saber distinguir quando uma vírgula é delimitadora e quando ela faz parte do dado em si. Esse é o cerne de qualquer problema sério com CSV. Já perdi horas depurando pipelines inteiros porque alguém exportou relatórios financeiros com valores em real usando vírgula decimal, enquanto o separador de campo também era vírgula. O resultado era uma bagunça de colunas deslocadas que só você percebia quando os totais não batiam.
Como resolver na prática
A primeira coisa que você precisa fazer é garantir que o parser reconheça o delimitador correto. Em Python, o módulo csv lida com isso nativamente desde que você passe os parâmetros certos. Não adianta confiar no comportamento padrão. O Python assume vírgula como delimitador e aspas duplas como quotechar por padrão, e esse comportamento padrão já cai em 70% dos arquivos que saem de ERPs brasileiros.
import csv
with open('arquivo.csv', 'r', encoding='utf-8') as f:
leitor = csv.reader(f, delimiter=';', quotechar='"')
for linha in leitor:
print(linha)
Essa configuração acima resolve a grande maioria dos casos porque a maioria dos sistemas brasileiros exporta usando ponto e vírgula como delimitador e vírgula dentro dos campos. Mude o delimiter para ponto e vírgula e o quotechar para aspas duplas, que você já elimina o cenário mais comum de colapso.
O caso que ninguém conta
Tem uma situação que as documentações não mencionam e que eu aprendi na prática custa caro. Quando o arquivo CSV vem de uma fonte interna com campos multilinha — ou seja, texto com quebra de linha dentro das aspas — o leitor do Python por padrão mantém a quebra, mas alguns sistemas downstream interpretam a linha quebrada como novo registro. Já precisei processar um arquivo de 400 mil linhas que na verdade tinha 280 mil registros reais porque os campos de observação continham parágrafos inteiros com quebras de linha. O workaround que eu uso atualmente é validar a contagem de campos por linha antes de processar. Se uma linha tem menos colunas que a média das linhas anteriores, é quase certo que uma quebra de linha foi interpretada como separador de registro. Nesse caso, faço o join manual das linhas órfãs até que o número de campos bata com o padrão.
👉 Clique no botão abaixo para saber mais sobre o assunto!
import csv def read_robust_csv(filepath): with open(filepath, 'r', encoding='utf-8') as f: leitor = csv.reader(f, delimiter=';', quotechar='"') linhas = list(leitor) if not linhas: return [] expected_cols = len(linhas[0]) resultado = [] atual = [] for linha in linhas: if len(linha) == expected_cols: if atual: resultado.append(atual) atual = linha else: atual.extend(linha) if atual: resultado.append(atual) return resultado[pre]>Essa função simples economiza noites de debugging. Ela não é perfeita, claro. Se você tiver campos intencionalmente incompletos no final de uma linha, o código vai empilhar errado. Mas na prática, arquivos bem formados têm a mesma quantidade de colunas em todas as linhas, e esse padrão funciona na grande maioria dos casos reais.
Alternativas quando o CSV não dá conta
Se o arquivo que você está tratando é realmente problemático — campos sem aspas, delimitadores inconsistentes, encoding estranho — o csv.reader do Python pode não ser suficiente. Nesse cenário, a biblioteca pandas com o parâmetro engine='python' resolve muitos casos, mas perde performance em arquivos grandes. Para arquivos acima de 500 MB, o pandas com engine='c' é mais rápido, mas não aceita todas as regras de parsing que o engine 'python' aceita. A alternativa que eu recomendo para casos extremos é usar o módulo regex para pré-processar o arquivo antes de passar para o parser. Você pode padronizar as aspas, remover quebras de linha dentro de campos delimitados e corrigir delimitadores inconsistentes com uma única passagem. Leva cerca de 3 minutos num arquivo de 2 GB que eu processei semana passada, contra 45 minutos tentando ajustar parâmetros de encoding e delimiter no pandas.
import re with open('arquivo.csv', 'r', encoding='utf-8') as f: conteudo = f.read() Remove quebras de linha dentro de campos entre aspas conteudo = re.sub(r'"([^"\n]*)\n([^"]*)"', r'"\1\2"', conteudo) with open('arquivo_corrigido.csv', 'w', encoding='utf-8') as f: f.write(conteudo)Esse pré-processamento com regex é uma solução paliativa. Ele não resolve problemas de encoding ou delimitadores faltando, mas corrige o erro mais frequente que eu vejo em produção: quebras de linha acidentais que quebram a estrutura do arquivo inteiro.
Limitações que você precisa saber
Nenhuma dessas abordagens funciona se o arquivo original já tiver os campos mal formatados sem aspas. Se o sistema origem escreveu algo como
João, Silva;32;São Paulo, SPsem colocar as aspas ao redor do campo que contém vírgula, não há mágica que resolva. A única saída nesse caso é conversar com a equipe que gera o arquivo e exigir que eles implementem o quote correto nos campos. Tentar adivinhar qual vírgula é delimitadora e qual é conteúdo em campos sem aspas é um problema NP-completo na prática, e você vai gastar mais tempo errando do que conseguindo uma solução confiável. Também vale lembrar que a solução com regex de pré-processamento quebra se o campo contiver aspas simples ou escape sequences que o regex não prevê. Em um projeto recente, encontrei um arquivo onde o campo texto usava aspas duplas escapadas com barra invertida — algo que o regex padrão não identifica — e tive que escrever um parser stateful personalizado. Levou duas horas e meia, mas depois disso o pipeline rodou limpo em produção.Download do script completo
Deixo abaixo o código consolidado que eu uso no meu dia a dia. Ele combina a validação de colunas, o pré-processamento com regex e a leitura robusta em uma única função reutilizável.
import csv import re def read_brazilian_csv(filepath, expected_cols=None): with open(filepath, 'r', encoding='utf-8') as f: conteudo = f.read() conteudo = re.sub(r'"([^"\n]*)\n([^"]*)"', r'"\1\2"', conteudo) linhas = [] for linha in conteudo.splitlines(): if linha.strip(): linhas.append(linha) if expected_cols is None: reader = csv.reader(linhas, delimiter=';', quotechar='"') all_rows = list(reader) if all_rows: expected_cols = len(all_rows[0]) resultado = [] atual = [] for linha in linhas: reader = csv.reader([linha], delimiter=';', quotechar='"') parsed = list(reader)[0] if len(parsed) == expected_cols: if atual: resultado.append(atual) atual = parsed else: atual.extend(parsed) if atual: resultado.append(atual) return resultadoPara usar, basta chamar a função passando o caminho do arquivo. Ela retorna uma lista de listas onde cada sublista é uma linha com o número correto de colunas. Se quiser salvar o resultado formatado, pode converter para DataFrame do pandas ou escrever de volta num CSV com o delimiter que preferir. O tempo médio de processamento num arquivo de 1 GB com 500 mil linhas é cerca de 40 segundos na minha máquina, o que é aceitável para um trabalho batch noturno.