mygems

Como comparar dois arquivos CSV e descobrir o que mudou

O diff compara linhas, então uma exportação reordenada parece reescrita. Compare por chave: o que é novo, o que sumiu e o que mudou.

Por uos ·

diff old.csv new.csv responde à pergunta errada. Ele compara linhas, então um arquivo ordenado de outro jeito volta como "tudo mudou", e uma linha em que um único campo foi corrigido volta como uma exclusão mais uma adição, e não como uma alteração.

O que você quer é uma comparação por chave: quais linhas são novas, quais sumiram e, nas linhas presentes nos dois arquivos, quais campos são diferentes. São três perguntas, e elas precisam de três respostas diferentes.

Comece pela estrutura

Antes de comparar o conteúdo, confira se os dois arquivos são comparáveis:

head -n 1 old.csv new.csv
wc -l old.csv new.csv

Um cabeçalho alterado é o motivo mais comum de comparações sem sentido. Se uma coluna foi adicionada, renomeada ou reordenada entre as duas exportações, toda comparação campo a campo perde o sentido até você levar isso em conta. O número de linhas é o segundo sinal: uma diferença de três é uma semana normal; uma diferença de 40.000 geralmente significa que uma exportação foi truncada ou filtrada.

A comparação de três perguntas em SQL

Escolha primeiro a sua chave: a coluna que identifica o mesmo registro nos dois arquivos. id, order_id, email. Se não houver nenhuma, vá para a última seção.

Linhas só no arquivo novo:

duckdb -c "SELECT * FROM read_csv('new.csv') n WHERE n.id NOT IN (SELECT id FROM read_csv('old.csv'))"

Linhas só no arquivo antigo:

duckdb -c "SELECT * FROM read_csv('old.csv') o WHERE o.id NOT IN (SELECT id FROM read_csv('new.csv'))"

Linhas nos dois, com um campo alterado:

duckdb -c "SELECT o.id, o.status old_status, n.status new_status FROM read_csv('old.csv') o JOIN read_csv('new.csv') n USING (id) WHERE o.status IS DISTINCT FROM n.status"

Usar IS DISTINCT FROM em vez de <> é o detalhe que importa: <> retorna NULL quando qualquer um dos lados é NULL, então um campo que passou de vazio para preenchido, ou de preenchido para vazio, some dos seus resultados sem avisar. Essa é a mudança que você provavelmente mais queria encontrar.

Para pegar mudanças em todas as colunas de uma vez, sem citá-las:

duckdb -c "SELECT * FROM read_csv('old.csv') EXCEPT SELECT * FROM read_csv('new.csv')"

EXCEPT compara linhas inteiras e ignora a ordem, o que o torna um diff muito melhor que o diff. Ele diz que uma linha mudou, não qual campo; por isso, combine-o com o join acima quando já souber quais linhas olhar.

Quando os arquivos só estão ordenados de outro jeito

Se você só precisa saber se o conteúdo é o mesmo, ordene e calcule o hash:

tail -n +2 old.csv | sort | md5sum
tail -n +2 new.csv | sort | md5sum

Mesmo hash, mesmo conteúdo, não importa a ordem das linhas. Um hash diferente não diz nada sobre o que mudou, mas leva um segundo e resolve a pergunta "essas duas exportações são a mesma?", que muitas vezes é a investigação inteira.

Para um diff por linhas que sobreviva à reordenação:

diff <(sort old.csv) <(sort new.csv)

Continua sendo baseado em linhas, então uma mudança de um campo ainda aparece como um par, mas pelo menos a ordenação deixa de dominar a saída.

Ferramentas dedicadas

csvdiff e daff comparam por chave e geram uma saída que diz "a coluna status mudou de A para B na linha 41", em vez de mostrar duas linhas. Se você faz isso com frequência, instale uma das duas. daff diff old.csv new.csv --output diff.html gera uma tabela colorida que dá para ler de relance, o que nenhuma das opções acima consegue.

Git, se os arquivos estiverem versionados. Definir um driver de diff para CSV em .gitattributes faz o git diff em um arquivo de dados passar de inútil a útil, e é uma mudança de duas linhas.

Excel, sendo honesto, é ruim nisso. A abordagem de sempre, um PROCV (VLOOKUP) por coluna para a outra planilha, funciona com arquivos pequenos, é quadrática nos grandes e produz uma grade de #N/D difícil de ler. A mesclagem do Power Query é muito melhor e quase ninguém a usa.

Lendo as diferenças no celular

A versão mais comum no dia a dia: duas exportações com uma semana de intervalo, no seu e-mail, e a pergunta é "o que mudou?", não "gere um relatório de conciliação".

O caminho prático não é um diff. Adicione uma coluna marcando a origem, empilhe os dois arquivos, ordene pela chave e leia os pares. Os campos alterados ficam lado a lado porque as duas versões de cada registro agora estão adjacentes. Para algumas dezenas de linhas, isso é mais rápido que qualquer ferramenta.

CSV File Viewer - Smart CSViPhone e iPad · Android

SQL direto no arquivo é a comparação inteira: um join pela chave com IS DISTINCT FROM encontra os campos alterados, e o assistente de IA cuida da versão da pergunta que você prefere fazer em palavras em vez de escrever.

O que ele faz

Números, datas e as diferenças falsas

Uma comparação costuma apontar um grande número de "mudanças" que não são. Descarte essas antes de investigar:

  • Espaços no final. "active" e "active " são diferentes. Compare valores com trim(), a menos que os espaços tenham significado, o que quase nunca acontece.
  • Formatação de números. 1000, 1,000 e 1000.00 são um número e três strings. Converta os dois lados para um tipo numérico antes de comparar.
  • Formatos de data. 01/02/2026 versus 2026-02-01 e, pior, 01/02/2026 significando fevereiro em um arquivo e janeiro em outro. Converta os dois para datas em vez de comparar texto.
  • Ruído de ponto flutuante. 0.1 + 0.2 não é 0.3 em nenhuma linguagem que você use. Arredonde para a precisão que importa antes de comparar decimais.
  • Codificação. Um nome que aparece como José em um arquivo e José no outro é uma diferença de codificação, não uma mudança nos dados.

Cada um desses pontos produz milhares de diferenças em um arquivo grande, e é assim que uma comparação acaba "mostrando tudo como alterado".

CSV Editor - Smart CSViPhone e iPad

Quando a comparação vira correção, como normalizar uma coluna, arredondar um decimal ou corrigir as linhas que realmente mudaram, uma coluna de fórmula aplica isso a todas as linhas, e o resultado é salvo dentro do CSV.

O que ele faz

Quando não há chave

Às vezes os arquivos não têm identificador: duas exportações de um log, duas listas de transações. Você tem duas opções, e ambas são concessões.

Montar uma chave composta com as colunas que juntas identificam uma linha: data mais valor mais descrição, por exemplo. Funciona quando a combinação é realmente única e falha em silêncio quando não é: duas transações idênticas no mesmo dia viram uma, e a comparação relata uma exclusão fantasma.

Comparar como multiconjuntos. Conte as ocorrências de cada linha distinta em cada arquivo e compare as contagens. É o que você precisa para logs e transações, em que a mesma linha aparece legitimamente mais de uma vez e a mudança relevante é quantas vezes:

duckdb -c "SELECT *, count(*) n FROM read_csv('old.csv') GROUP BY ALL"

Execute nos dois arquivos e compare as colunas n. É a única abordagem que acerta em dados com muitas duplicatas.

Perguntas

Como comparo dois arquivos CSV e encontro as diferenças?
Compare por chave, não por linha. Faça três perguntas separadas: quais chaves estão só no arquivo novo, quais só no antigo e, nas chaves presentes nos dois, quais campos são diferentes. Em SQL, são dois anti-joins e um join com `IS DISTINCT FROM` por coluna. Um `diff` simples relata uma mudança de um campo como uma exclusão mais uma adição, e relata um arquivo reordenado como totalmente alterado.
Por que o diff diz que todas as linhas mudaram se os dados parecem iguais?
Quase sempre porque as duas exportações estão ordenadas de forma diferente. O `diff` compara as linhas em ordem, então um arquivo reordenado parece totalmente reescrito. `diff <(sort old.csv) <(sort new.csv)` elimina esse efeito, e `SELECT * FROM old EXCEPT SELECT * FROM new` ignora completamente a ordem das linhas.
Como comparo dois arquivos CSV no Excel?
Use a mesclagem do Power Query em vez de PROCV (VLOOKUP). Um PROCV por coluna para a outra planilha funciona com algumas centenas de linhas, é quadrático em arquivos grandes e produz uma grade de #N/D difícil de ler. O Power Query faz o join por uma chave, mantém lado a lado as duas versões de cada coluna e é atualizado quando os arquivos são substituídos.
Por que minha comparação de CSV mostra milhares de mudanças falsas?
Por diferenças de formatação que não são diferenças de dados: espaços no final, separadores de milhar, `1000` versus `1000.00`, dois formatos de data, ruído de ponto flutuante e codificações de texto incompatíveis. Normalize os dois lados (remover espaços, converter números para tipo numérico, interpretar datas como datas, arredondar decimais) antes de comparar; do contrário, cada um desses pontos aparece como mudança em cada linha afetada.
Como comparo arquivos CSV que não têm coluna de ID?
Ou você monta uma chave composta com as colunas que juntas identificam uma linha, ou compara os arquivos como multiconjuntos, contando as ocorrências de cada linha distinta dos dois lados e comparando as contagens. A segunda opção é a certa para logs e transações, em que a mesma linha pode se repetir legitimamente e a mudança que importa é quantas vezes ela aparece.
Qual é a forma mais rápida de saber se dois arquivos CSV são idênticos?
Tire o cabeçalho, ordene e calcule o hash: `tail -n +2 file.csv | sort | md5sum` em cada um. Hashes iguais significam conteúdo idêntico, não importa a ordem das linhas. Isso não diz o que é diferente, mas leva um segundo e muitas vezes resolve a dúvida que você tinha, que era saber se as duas exportações são a mesma.

uos — Desenvolve o CSV Editor e o Smart CSV Viewer e já conciliou tantos pares de exportações que desconfia de qualquer diff por linhas.