Como unir arquivos CSV com colunas diferentes sem perder dados
Publicado em 2026-08-29T20:13:00Z · atualizado em 2026-08-29T19:13:05+00:00
Unir vários arquivos CSV parece uma tarefa de copiar e colar até o primeiro arquivo chamar a coluna de `Código`, o segundo usar `codigo_produto` e o terceiro nem possuir esse campo. Depois surgem zeros à esquerda que desaparecem, datas inve
Como unir arquivos CSV com colunas diferentes sem perder dados
Unir vários arquivos CSV parece uma tarefa de copiar e colar até o primeiro arquivo chamar a coluna de Código, o segundo usar codigo_produto e o terceiro nem possuir esse campo. Depois surgem zeros à esquerda que desaparecem, datas invertidas, decimais interpretados como texto e linhas que deixam de caber na planilha. O resultado pode até abrir, mas isso não prova que os dados continuam corretos.
Este guia apresenta um processo reproduzível para consolidar CSVs com esquemas diferentes. A estratégia é separar descoberta, mapeamento, leitura, padronização, união e validação. O exemplo usa Python com a biblioteca padrão e uma alternativa com pandas. Quem prefere trabalhar no navegador pode começar pelo Auditor de CSV, usar o Conversor de dados e conferir resultados entre arquivos no IATNGRID.
Legenda: a união só acontece depois que o esquema de cada origem foi compreendido.
Entenda por que “empilhar os arquivos” não basta
Há dois tipos de combinação que costumam ser confundidos. Concatenar significa colocar linhas de arquivos equivalentes uma abaixo da outra. Relacionar significa combinar colunas de tabelas diferentes usando uma chave, como código do produto. Neste artigo, o foco é concatenação vertical: vários arquivos representam o mesmo tipo de registro, mas seus cabeçalhos e formatos variam.
Considere três arquivos:
loja_a.csv
codigo;produto;valor
00123;Teclado;89,90
loja_b.csv
codigo_produto,nome,preco,estoque
00456,Mouse,49.50,12
loja_c.csv
SKU;DESCRICAO;PRECO_VENDA
00077;Cabo USB;19,90
Empilhar os textos criaria três cabeçalhos no meio do arquivo e misturaria delimitadores. Mesmo que um programa aceite o resultado, produto, nome e DESCRICAO seriam tratados como campos diferentes. O processo correto precisa declarar que eles representam a mesma informação.
Também é preciso decidir o esquema final. Para o exemplo:
codigo,produto,valor,estoque,arquivo_origem
estoque ficará vazio quando a origem não oferecer o dado. Isso é diferente de preencher 0, que afirmaria existir estoque zero. Ausência de informação e valor zero não são equivalentes.
Preserve os arquivos originais e construa um inventário
Nunca comece sobrescrevendo as fontes. Crie três diretórios:
projeto-csv/
entrada/
saida/
logs/
Coloque cópias dos arquivos em entrada. O script deve escrever apenas em saida. Registre para cada arquivo: nome, tamanho, codificação provável, delimitador, cabeçalhos, quantidade de linhas e data de processamento.
Você pode gerar um hash para provar qual arquivo foi processado:
python -c "import hashlib, pathlib; p=pathlib.Path('entrada/loja_a.csv'); print(hashlib.sha256(p.read_bytes()).hexdigest())"
O hash não avalia qualidade, mas identifica os bytes. Se alguém substituir loja_a.csv por outra versão com o mesmo nome, o hash muda. Isso torna a consolidação auditável.
Antes de abrir no Excel, visualize as primeiras linhas como texto. Planilhas podem interpretar automaticamente códigos, datas e números, alterando a percepção do arquivo. No terminal:
python -c "from pathlib import Path; print(Path('entrada/loja_a.csv').read_text(encoding='utf-8-sig').splitlines()[:5])"
Se a leitura falhar, não tente encodings aleatórios até “abrir”. Descubra a origem do arquivo. UTF-8 com ou sem BOM e encodings legados como Windows-1252 precisam ser tratados de forma explícita. Uma substituição silenciosa de caracteres pode transformar nomes diferentes em textos aparentemente iguais.
Detecte delimitador e codificação com cautela
O módulo csv oferece Sniffer, que tenta inferir o dialeto a partir de uma amostra. Ele é útil como sugestão, não como verdade absoluta:
import csv
from pathlib import Path
caminho = Path("entrada/loja_a.csv")
amostra = caminho.read_text(encoding="utf-8-sig")[:8192]
dialeto = csv.Sniffer().sniff(amostra, delimiters=",;\t|")
print("delimitador:", repr(dialeto.delimiter))
print("tem cabeçalho:", csv.Sniffer().has_header(amostra))
Uma descrição com muitas vírgulas pode enganar a detecção. Por isso, restrinja os delimitadores possíveis e confira se a quantidade de campos permanece estável nas primeiras linhas. Se o fornecedor documenta ponto e vírgula, prefira a configuração documentada à inferência.
Para codificação, uma política simples e explícita é melhor que “detectar tudo”: tente utf-8-sig; se falhar, registre o erro e exija que a origem informe a codificação. Em uma migração conhecida, você pode autorizar uma lista curta, como utf-8-sig e cp1252, salvando no log qual foi usada.
def ler_texto(caminho):
erros = []
for encoding in ("utf-8-sig", "cp1252"):
try:
return caminho.read_text(encoding=encoding), encoding
except UnicodeDecodeError as exc:
erros.append(f"{encoding}: {exc}")
raise UnicodeError("; ".join(erros))
Não use errors="ignore" em dados importantes. Ignorar bytes inválidos pode apagar caracteres sem aviso.
Crie um mapa de cabeçalhos em vez de renomear no improviso
O mapeamento precisa ser declarativo e revisável:
MAPAS = {
"loja_a.csv": {
"codigo": "codigo",
"produto": "produto",
"valor": "valor",
},
"loja_b.csv": {
"codigo_produto": "codigo",
"nome": "produto",
"preco": "valor",
"estoque": "estoque",
},
"loja_c.csv": {
"SKU": "codigo",
"DESCRICAO": "produto",
"PRECO_VENDA": "valor",
},
}
Os nomes à esquerda são os cabeçalhos originais; os da direita pertencem ao esquema final. Esse formato evita alterar a fonte e permite revisar cada decisão.
Antes de usar o mapa, normalize apenas aspectos que não carregam significado, como espaços externos e BOM no primeiro cabeçalho:
def limpar_cabecalho(nome):
return nome.replace("\ufeff", "").strip()
Não remova acentos, pontuação e espaços internos automaticamente para “fazer bater”. valor total e valor_total talvez sejam equivalentes, mas valor bruto e valor líquido não são. Um algoritmo baseado em similaridade pode sugerir pares, mas a aprovação deve ser humana.
Defina ainda campos obrigatórios. Se codigo e produto são essenciais, interrompa o processamento da origem que não consegue fornecê-los. Continuar com milhares de linhas sem chave pode gerar um arquivo grande e inútil.
Leia tudo inicialmente como texto
CSV não armazena tipos formais. O valor 00123 pode ser código, não número. Se você o converter para inteiro, vira 123 e talvez deixe de coincidir com outro sistema. Datas como 01/02/2026 são ambíguas fora do contexto. Valores como 1.234,56 misturam separadores locais.
Na fase de ingestão, preserve as strings:
import csv
with open("entrada/loja_a.csv", encoding="utf-8-sig", newline="") as arquivo:
leitor = csv.DictReader(arquivo, delimiter=";")
for linha in leitor:
print(repr(linha["codigo"])) # '00123'
O parâmetro newline="" é recomendado pela documentação do módulo csv, pois permite que ele trate corretamente as convenções de quebra de linha e campos com quebras entre aspas.
Converta apenas campos cujo tipo e formato foram definidos. Para dinheiro em padrão brasileiro:
from decimal import Decimal, InvalidOperation
def decimal_br(texto):
valor = texto.strip()
if not valor:
return None
normalizado = valor.replace(".", "").replace(",", ".")
try:
return Decimal(normalizado)
except InvalidOperation:
raise ValueError(f"valor monetário inválido: {texto!r}")
Decimal evita certas aproximações binárias de float, mas a regra de conversão só é válida quando a origem garante o formato brasileiro. Para o arquivo com 49.50, use uma função específica da origem. Não aplique a mesma limpeza a formatos diferentes.
Faça a união com rastreabilidade de origem
Este script reduzido lê os três arquivos, aplica mapas e escreve um resultado uniforme:
import csv
from pathlib import Path
ENTRADA = Path("entrada")
SAIDA = Path("saida/consolidado.csv")
CAMPOS = ["codigo", "produto", "valor", "estoque", "arquivo_origem"]
CONFIG = {
"loja_a.csv": {"delimiter": ";", "encoding": "utf-8-sig"},
"loja_b.csv": {"delimiter": ",", "encoding": "utf-8-sig"},
"loja_c.csv": {"delimiter": ";", "encoding": "utf-8-sig"},
}
def converter(nome, linha):
mapa = MAPAS[nome]
final = {campo: "" for campo in CAMPOS}
for original, destino in mapa.items():
final[destino] = (linha.get(original) or "").strip()
final["arquivo_origem"] = nome
return final
SAIDA.parent.mkdir(exist_ok=True)
with SAIDA.open("w", encoding="utf-8", newline="") as destino:
escritor = csv.DictWriter(destino, fieldnames=CAMPOS)
escritor.writeheader()
for nome, opcoes in CONFIG.items():
with (ENTRADA / nome).open(
"r", encoding=opcoes["encoding"], newline=""
) as origem:
leitor = csv.DictReader(origem, delimiter=opcoes["delimiter"])
leitor.fieldnames = [limpar_cabecalho(c) for c in leitor.fieldnames]
faltantes = {"codigo", "produto"} - set(MAPAS[nome].values())
if faltantes:
raise ValueError(f"{nome}: campos obrigatórios sem mapa: {faltantes}")
for linha in leitor:
escritor.writerow(converter(nome, linha))
O campo arquivo_origem é essencial. Quando uma linha estranha aparecer no consolidado, você saberá onde procurá-la. Em projetos maiores, adicione linha_origem, data de processamento e hash do arquivo.
Esse exemplo preserva valor como texto. A conversão para um formato canônico pode ser feita dentro de converter, usando uma função diferente para cada origem. Grave o resultado com ponto decimal ou outro padrão definido para o destino, sem arredondar silenciosamente.
Use pandas quando a transformação justificar a dependência
Com pandas, a leitura e concatenação ficam compactas:
import pandas as pd
a = pd.read_csv("entrada/loja_a.csv", sep=";", dtype=str, keep_default_na=False)
b = pd.read_csv("entrada/loja_b.csv", sep=",", dtype=str, keep_default_na=False)
c = pd.read_csv("entrada/loja_c.csv", sep=";", dtype=str, keep_default_na=False)
a = a.rename(columns={"codigo":"codigo", "produto":"produto", "valor":"valor"})
b = b.rename(columns={"codigo_produto":"codigo", "nome":"produto", "preco":"valor"})
c = c.rename(columns={"SKU":"codigo", "DESCRICAO":"produto", "PRECO_VENDA":"valor"})
a["arquivo_origem"] = "loja_a.csv"
b["arquivo_origem"] = "loja_b.csv"
c["arquivo_origem"] = "loja_c.csv"
final = pd.concat([a, b, c], join="outer", ignore_index=True, sort=False)
final.to_csv("saida/consolidado.csv", index=False, encoding="utf-8")
join="outer" preserva a união das colunas; onde uma origem não possui o campo, o resultado recebe valor ausente. dtype=str reduz conversões automáticas, e keep_default_na=False impede que certos textos sejam transformados automaticamente em valores ausentes. Ainda assim, confira o comportamento na versão utilizada.
Não confunda concat com merge. concat empilha os registros. merge relaciona linhas por uma chave e pode multiplicar registros quando existem duplicidades nos dois lados. Se sua necessidade é buscar informações de uma tabela em outra, projete uma junção separada e valide cardinalidade.
Para arquivos maiores que a memória, leia em blocos com chunksize, escreva incrementalmente ou use uma ferramenta colunar. Mas não otimize antes de medir. A biblioteca padrão processa linha a linha e pode ser suficiente para muitos casos.
Valide o consolidado com invariantes mensuráveis
Um arquivo que foi gerado sem exceção não está necessariamente correto. Defina invariantes antes do processamento.
Primeiro, a contagem:
linhas_saida = linhas_loja_a + linhas_loja_b + linhas_loja_c
Desconte apenas cabeçalhos. Se o objetivo é uma concatenação sem filtros, qualquer diferença exige explicação.
Depois, confirme campos obrigatórios:
if not final["codigo"].str.strip().ne("").all():
raise ValueError("há códigos vazios no resultado")
Cheque distribuição por origem:
print(final["arquivo_origem"].value_counts(dropna=False))
Verifique duplicidades sem removê-las automaticamente:
duplicados = final[final.duplicated(subset=["codigo"], keep=False)]
duplicados.to_csv("saida/duplicados_para_revisao.csv", index=False)
Um código repetido pode ser erro, versões diferentes ou registros legítimos. A regra de negócio decide. Apagar duplicidades com drop_duplicates() sem entender a causa costuma esconder divergências.
Para dinheiro, compare soma e faixa por origem antes e depois da conversão. Para datas, contabilize quantas foram interpretadas e quantas ficaram inválidas. Para categorias, liste valores novos. Use amostras das primeiras e últimas linhas de cada arquivo, não apenas do início do consolidado.
Legenda: toda diferença entre entradas e saída precisa aparecer como regra ou rejeição documentada.
Gere um arquivo de rejeições em vez de perder linhas
Quando uma linha contém valor monetário inválido ou campo obrigatório vazio, há três escolhas: interromper tudo, aceitar o problema ou separar a linha. Para lotes recorrentes, um arquivo de rejeições costuma equilibrar continuidade e controle.
Registre:
arquivo_origem,linha_origem,motivo,conteudo_original
Proteja dados sensíveis antes de gravar o conteúdo completo. Em alguns contextos, basta registrar a chave e o motivo. O total deve fechar:
linhas_lidas = linhas_aceitas + linhas_rejeitadas
Se uma origem mudou o cabeçalho, considere falhar o arquivo inteiro. Tentar adivinhar o novo significado pode colocar valores na coluna errada. Uma interrupção clara é melhor que um consolidado silenciosamente incorreto.
O log também deve informar versão do mapeamento e do script. Assim, é possível explicar por que o mesmo arquivo teria resultado diferente após uma regra nova.
Abra no Excel sem sacrificar códigos e acentos
O Excel pode interpretar 00123 como número e mostrar 123, mesmo que o CSV ainda contenha os zeros. Também pode usar configuração regional para escolher delimitador e decimal. Para inspeção, prefira importar o arquivo pela ferramenta de dados e declarar os tipos das colunas, em vez de abrir com duplo clique.
Se o público depende do Excel, considere entregar também .xlsx com colunas formatadas como texto. CSV continua útil como formato interoperável e simples, mas não armazena formatação, largura, tipo explícito ou múltiplas abas.
Não inclua caracteres como = no início de células vindas de fonte não confiável sem avaliar o destino. Algumas planilhas podem interpretar fórmulas ao abrir. Se o conteúdo é texto e pode começar com =, +, - ou @, adote uma política de neutralização compatível com o uso, documentando a transformação. Evite uma substituição indiscriminada em campos que realmente representam números negativos.
Teste o CSV em pelo menos dois leitores: um editor de texto ou parser e a aplicação de destino. Se o parser lê 10 mil linhas e o Excel mostra menos, investigue limite, filtro ou interpretação; não altere o arquivo até entender a diferença.
Automatize com configuração, relatório e execução repetível
Quando o lote se repete, retire os detalhes do código e coloque em configuração versionada:
{
"loja_a.csv": {
"encoding": "utf-8-sig",
"delimiter": ";",
"columns": {
"codigo": "codigo",
"produto": "produto",
"valor": "valor"
}
}
}
O programa deve recusar arquivos desconhecidos ou colocá-los em quarentena para revisão. Produza um relatório com hashes, contagens, colunas encontradas, campos ausentes, rejeições e tempo. Execute primeiro em uma pasta de teste com cópias pequenas.
Inclua testes automatizados para os detalhes que mais quebram:
- código
00123continua com cinco caracteres; - descrição com delimitador entre aspas permanece em uma célula;
- descrição com quebra de linha entre aspas continua sendo um registro;
- acentos são preservados;
- vazio não vira zero;
- decimal brasileiro e decimal com ponto seguem regras distintas;
- coluna extra é preservada ou rejeitada conforme política;
- cabeçalho ausente interrompe a origem;
- contagem final fecha.
Com isso, uma mudança de biblioteca ou de fornecedor deixa de ser descoberta meses depois por um relatório divergente.
Checklist final para unir CSVs com segurança
- Os originais foram preservados sem sobrescrita?
- Há inventário com hash, encoding, delimitador, cabeçalhos e linhas?
- O esquema final está definido antes da união?
- Cada cabeçalho possui um mapa aprovado?
- Campos obrigatórios interrompem a origem quando ausentes?
- Códigos, documentos e chaves são lidos como texto?
- Datas e decimais usam regras explícitas por origem?
- Vazio, zero e
nullpermanecem semanticamente diferentes? - Cada linha registra arquivo e, se possível, número de origem?
- A soma das entradas fecha com aceitas e rejeitadas?
- Duplicidades são relatadas antes de qualquer remoção?
- O resultado foi testado no parser e na aplicação de destino?
- Existe relatório legível para revisão humana?
O ponto central é simples: consolidar arquivos não é apenas juntar linhas. É traduzir esquemas mantendo significado e rastreabilidade. Quando o processo preserva as fontes, declara mapas e fecha contagens, você pode corrigir uma regra sem perder a história do dado.
Documentação primária e referências técnicas
- RFC 4180 — Common Format and MIME Type for CSV Files
- Python — módulo csv
- pandas — read_csv
- pandas — concat
- pandas — guia de merge, join, concat e compare
Nota sobre a produção deste conteúdo
Rascunho inédito produzido com apoio de IA generativa após auditoria das pautas existentes do IATechNerds. O procedimento foi fundamentado no formato descrito pela RFC 4180, no módulo csv da biblioteca padrão do Python e na documentação oficial do pandas. Os exemplos usam dados fictícios e precisam ser executados e revisados por uma pessoa antes da publicação.
Camada extra: como tomar uma decisão melhor neste cenário
Um artigo sobre Como unir arquivos CSV com colunas diferentes sem perder dados fica mais útil quando separa a solução imediata da decisão que continua válida depois que a tela, a versão do software ou o dispositivo muda.
Unir vários arquivos CSV parece uma tarefa de copiar e colar até o primeiro arquivo chamar a coluna de `Código`, o segundo usar `codigo_produto` e o terceiro nem possuir esse campo. Depois surgem zeros à esquerda que desaparecem, datas inve A ideia central pode ser testada com quatro perguntas: o que foi observado, qual hipótese explica o sintoma, qual mudança é reversível e qual evidência prova que funcionou. Esse encadeamento evita que uma coincidência seja confundida com causa e torna o procedimento repetível por outra pessoa.
Integridade vem antes da aparência
Em CSV, Python, pandas, qualidade de dados, o arquivo “abrir” não significa que os dados chegaram intactos. Antes de transformar, separe uma amostra com um registro normal, uma duplicidade, um valor vazio, um código com zero à esquerda e um caso de limite. Essa amostra funciona como teste de regressão: qualquer importação, limpeza ou conversão precisa preservar o que é informação e alterar somente o que é representação.
Compare contagem de linhas, quantidade de chaves únicas, somas de controle quando fizer sentido e tipos esperados. Datas, identificadores e casas decimais merecem atenção especial porque podem parecer visualmente corretos e ainda assim mudar de significado. Guarde o original sem sobrescrever e documente a regra aplicada; isso permite voltar atrás sem depender da memória.
Uma matriz simples para não misturar problemas
| Camada | Pergunta | Validação |
|---|---|---|
| Estrutura | Colunas e registros continuam presentes? | Contagens e cabeçalhos |
| Tipo | Código, data e número mantiveram o significado? | Amostra com casos-limite |
| Conteúdo | Houve perda, truncamento ou duplicação? | Comparação antes/depois |
Quando automatizar
Automação vale a pena quando a regra já é clara e o teste consegue detectar erro. Se ainda existem exceções não descritas, automatizar apenas faz o erro chegar mais rápido a mais linhas. Comece com uma amostra pequena, registre a saída esperada e só então escale para o conjunto inteiro.
Checklist de fechamento
- Registre o estado inicial antes de alterar qualquer coisa.
- Faça uma mudança por rodada e anote o efeito.
- Teste um caso normal, um caso-limite e o cenário que originalmente falhou.
- Confirme que a solução continua válida depois de reiniciar, reabrir ou repetir o fluxo.
- Guarde uma forma de voltar ao estado anterior quando a alteração for destrutiva.
Esse método acrescenta profundidade sem transformar o artigo em teoria abstrata: o leitor entende por que cada passo existe e como reconhecer quando a situação exige uma decisão diferente.
