Como comparar dois textos (diff) no terminal e em código
Um diff mostra o que mudou entre dois textos: linhas que saíram, que entraram e que ficaram iguais. Este guia mostra como comparar arquivos pelo terminal, como ler o formato unificado usado pelo git, como gerar diferenças em JavaScript e Python, e por que quebras de linha do Windows fazem tudo parecer diferente.
Pelo terminal
diff -u antigo.txt novo.txt: o comando clássico do Linux e do macOS, no formato unificado.git diff --no-index antigo.txt novo.txt: usa o diff do git em quaisquer dois arquivos, mesmo fora de um repositório, e funciona também no Windows com o Git instalado.git diff --word-diff: mostra as palavras que mudaram dentro da linha, bom para textos longos.git diff -w: ignora diferenças só de espaços.
Para comparar sem instalar nada, cole os dois textos no diff de texto, que compara por linha, palavra ou caractere no navegador.
Como ler o formato unificado
--- a/arquivoe+++ b/arquivo: o texto antigo e o novo.@@ -3,4 +3,5 @@: o trecho começa na linha 3 do antigo (4 linhas) e na linha 3 do novo (5 linhas).- Linhas com
-saíram, com+entraram, e com espaço são contexto, iguais nos dois. O padrão mostra 3 linhas de contexto. \ No newline at end of file: uma das versões não termina com quebra de linha.
Uma linha alterada aparece como uma remoção seguida de uma adição. O diff trabalha com linhas inteiras, a não ser que você peça comparação por palavra.
Como o diff encontra as mudanças
O diff procura a maior sequência de linhas que aparece, na mesma ordem, nos dois textos (a maior subsequência comum). Tudo que fica fora dela foi removido ou adicionado. O git usa por padrão o algoritmo de Myers, que chega ao mesmo tamanho mínimo de mudanças com menos memória.
A versão abaixo monta a tabela completa, o que usa memória proporcional às linhas de um texto vezes as do outro. Para arquivos com poucos milhares de linhas, basta. Para mais que isso, use o pacote diff do npm ou o próprio git.
// Diferença linha a linha pela maior subsequência comum (LCS).
// Exata e curta; para arquivos com milhares de linhas, use o pacote diff ou o git.
function diffLinhas(antes, depois) {
// \r\n (Windows) e \n (Linux) viram a mesma coisa, senão toda linha parece alterada
const a = antes.replace(/\r\n?/g, '\n').split('\n');
const b = depois.replace(/\r\n?/g, '\n').split('\n');
// lcs[i][j]: quantas linhas em comum há entre a[i..] e b[j..]
const lcs = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
for (let i = a.length - 1; i >= 0; i--) {
for (let j = b.length - 1; j >= 0; j--) {
lcs[i][j] = a[i] === b[j]
? lcs[i + 1][j + 1] + 1
: Math.max(lcs[i + 1][j], lcs[i][j + 1]);
}
}
const saida = [];
let i = 0;
let j = 0;
while (i < a.length || j < b.length) {
if (i < a.length && j < b.length && a[i] === b[j]) {
saida.push(` ${a[i++]}`);
j++;
} else if (i < a.length && (j === b.length || lcs[i + 1][j] >= lcs[i][j + 1])) {
saida.push(`- ${a[i++]}`);
} else {
saida.push(`+ ${b[j++]}`);
}
}
return saida;
} Devolve cada linha com " " (igual), "- " (saiu) ou "+ " (entrou).
Em Python, com difflib
O módulo difflib já vem com o Python. O unified_diff gera o mesmo formato do git, que o comando patch aplica, e o SequenceMatcher dá uma nota de semelhança entre 0 e 1.
import difflib
def diff_unificado(antes: str, depois: str, nome: str = "arquivo") -> str:
"""O mesmo formato do git diff e do comando patch."""
linhas = difflib.unified_diff(
antes.splitlines(),
depois.splitlines(),
fromfile=f"a/{nome}",
tofile=f"b/{nome}",
lineterm="",
)
return "\n".join(linhas)
def semelhanca(antes: str, depois: str) -> float:
# De 0 a 1 (1 = iguais). Serve para achar textos parecidos, não para gerar patch
return difflib.SequenceMatcher(None, antes, depois).ratio() semelhanca("casa", "cama") dá 0,75.
Cuidados
- Quebras de linha: um arquivo salvo no Windows usa
\r\ne um do Linux usa\n. Sem normalizar, todas as linhas aparecem como diferentes. O exemplo em JavaScript normaliza antes de comparar. - Espaços no fim da linha e tabulação trocada por espaços também contam como mudança. Use a opção de ignorar espaços quando só o conteúdo importa.
- Para comparar JSON, compare os dados e não o texto: a ordem das chaves muda o texto sem mudar o conteúdo. Veja como comparar dois JSON.
- Arquivos binários, como imagens e PDFs, não têm linhas: compare pelo hash, como no guia de SHA-256 de arquivo.
Achou um erro neste guia? Avise por e-mail.
Ferramentas para usar junto
Perguntas frequentes
Como comparar dois arquivos fora de um repositório git?
Use git diff --no-index arquivo1 arquivo2. Ele funciona com quaisquer dois arquivos, sem precisar de repositório, e mostra o resultado no formato unificado.
Por que o diff mostra todas as linhas como diferentes?
Quase sempre é a quebra de linha: um arquivo usa \r\n (Windows) e o outro \n (Linux e macOS). Converta os dois para o mesmo formato antes de comparar.
O que significa @@ no diff?
É o cabeçalho de cada trecho. @@ -3,4 +3,5 @@ quer dizer que o trecho começa na linha 3 do arquivo antigo, com 4 linhas, e na linha 3 do novo, com 5 linhas.
Como comparar textos em Python?
Use o módulo difflib, que vem com o Python. O unified_diff gera diferenças no formato do git, e o SequenceMatcher mede quanto dois textos se parecem.