difflib no Python: compare textos e arquivos

Publicado em: 01/08/2026
Tempo de leitura: 7 minutos
Documentos de texto representando comparação de versões com difflib no Python

Comparar duas versões de um texto é útil em revisão, auditoria, migrações, testes, sincronização e ferramentas de linha de comando. Em vez de apenas descobrir que os conteúdos são diferentes, muitas aplicações precisam mostrar quais linhas foram removidas, adicionadas ou alteradas. O módulo difflib no Python oferece comparadores de sequências, diffs legíveis, sugestões por similaridade e relatórios HTML sem dependências externas.

Neste guia, você aprenderá a usar SequenceMatcher, get_close_matches(), unified_diff(), context_diff(), ndiff(), restore() e HtmlDiff. O conteúdo complementa nossos artigos sobre listas em Python, slicing, arquivos temporários, collections e leitura de arquivos de texto.

O que difflib compara

difflib trabalha com sequências cujos elementos são hashable. Isso inclui strings, listas de linhas e listas de tokens.

from difflib import SequenceMatcher

antigo = "Python é simples"
novo = "Python é muito simples"

matcher = SequenceMatcher(None, antigo, novo)
print(matcher.ratio())

O algoritmo procura blocos contíguos comuns e aplica a mesma ideia recursivamente às partes restantes. Ele prioriza correspondências que parecem naturais para pessoas, mas não promete a sequência mínima de edições.

Similaridade com ratio()

ratio() retorna um valor entre 0 e 1. Valores próximos de 1 indicam grande semelhança.

from difflib import SequenceMatcher

similaridade = SequenceMatcher(None, "configuracao", "configuração").ratio()
print(round(similaridade, 3))

Esse número não é uma probabilidade e não possui um limiar universal. Escolha o corte com dados reais, avaliando falsos positivos e falsos negativos.

A ordem dos argumentos pode influenciar

A documentação oficial de difflib alerta que ratio() pode produzir valores diferentes ao inverter as sequências.

print(SequenceMatcher(None, "tide", "diet").ratio())
print(SequenceMatcher(None, "diet", "tide").ratio())

Se sua regra precisa ser simétrica, calcule nas duas direções ou use uma métrica desenhada explicitamente para isso.

quick_ratio() e real_quick_ratio()

Esses métodos retornam limites superiores mais rápidos para ratio().

matcher = SequenceMatcher(None, "abcd", "bcde")
print(matcher.real_quick_ratio())
print(matcher.quick_ratio())
print(matcher.ratio())

Em uma busca grande, você pode descartar candidatos cuja estimativa já fica abaixo do limiar e calcular a razão completa apenas para os restantes.

Encontrando blocos correspondentes

get_matching_blocks() informa posições e tamanhos dos blocos iguais.

matcher = SequenceMatcher(None, "abxcd", "abcd")
for bloco in matcher.get_matching_blocks():
    print(bloco)

O último bloco é sempre uma sentinela de tamanho zero. Essa API ajuda a destacar trechos ou construir visualizações próprias.

Transformações com get_opcodes()

get_opcodes() descreve como transformar a primeira sequência na segunda.

a = "qabxcd"
b = "abycdf"
matcher = SequenceMatcher(None, a, b)

for tag, i1, i2, j1, j2 in matcher.get_opcodes():
    print(tag, a[i1:i2], b[j1:j2])

As tags possíveis são equal, replace, delete e insert. Essa saída é mais estruturada que um diff textual e pode alimentar uma interface gráfica.

Comparando um item com muitos

SequenceMatcher armazena informações da segunda sequência. Quando um catálogo fixo será comparado com várias entradas, configure-o uma vez.

matcher = SequenceMatcher(None)
matcher.set_seq2("configuração")

for entrada in ["configuracao", "config", "configurações"]:
    matcher.set_seq1(entrada)
    print(entrada, matcher.ratio())

Esse reaproveitamento pode reduzir trabalho em corretores de digitação e normalizadores.

Sugestões com get_close_matches()

get_close_matches() retorna os candidatos mais parecidos acima de um corte.

from difflib import get_close_matches

comandos = ["iniciar", "instalar", "inspecionar", "interromper"]
print(get_close_matches("instlar", comandos, n=3, cutoff=0.6))

É útil em CLIs, validação de campos e mensagens “você quis dizer?”. Não use como busca semântica: a função compara forma da sequência, não significado.

O parâmetro autojunk

Em sequências com pelo menos 200 elementos, a heurística automática trata itens muito repetidos como “populares” e reduz seu peso.

matcher = SequenceMatcher(None, seq_a, seq_b, autojunk=False)

Desative autojunk quando a repetição é significativa, como DNA, logs estruturados ou listas de códigos. Compare desempenho e qualidade, pois desativar pode aumentar custo.

Elementos considerados junk

O primeiro argumento de SequenceMatcher pode ser uma função que marca elementos sem interesse.

matcher = SequenceMatcher(
    lambda caractere: caractere in " \t",
    "nome = valor",
    "nome=valor",
)

Junk ajuda a encontrar pontos de sincronização, mas não remove diferenças da saída. Se espaços precisam ser totalmente ignorados, normalize os textos antes de comparar e preserve uma cópia original para exibição.

Diff unificado

unified_diff() produz o formato conhecido de patches e revisões de código.

from difflib import unified_diff

antes = "linha 1\nlinha antiga\nlinha 3\n".splitlines(keepends=True)
depois = "linha 1\nlinha nova\nlinha 3\n".splitlines(keepends=True)

diff = unified_diff(
    antes,
    depois,
    fromfile="antes.txt",
    tofile="depois.txt",
)
print("".join(diff))

A função retorna um gerador. Materialize em lista apenas quando realmente precisar reutilizar ou contar as linhas.

Preservando quebras de linha

Use splitlines(keepends=True) ao comparar arquivos. As linhas de controle do diff usam quebra de linha por padrão para funcionar com writelines().

Quando as entradas não possuem terminadores, passe lineterm="" para evitar uma mistura de linhas com e sem quebra.

Diff contextual

context_diff() apresenta mudanças em blocos antes/depois.

from difflib import context_diff

for linha in context_diff(antes, depois, fromfile="a", tofile="b", n=2):
    print(linha, end="")

O parâmetro n controla quantas linhas iguais cercam cada mudança. Um contexto maior ajuda revisão, mas aumenta o relatório.

ndiff() para diferenças detalhadas

ndiff() marca cada linha com um prefixo:

  • - : exclusiva da primeira sequência;
  • + : exclusiva da segunda;
  • : comum;
  • ? : guia visual para diferenças internas.
from difflib import ndiff

resultado = list(ndiff(antes, depois))
print("".join(resultado))

Linhas iniciadas por ? não existem nos textos originais e podem ficar confusas com tabs e espaços.

Restaurando textos com restore()

Um delta de ndiff() pode restaurar qualquer uma das versões.

from difflib import restore

original = "".join(restore(resultado, 1))
modificado = "".join(restore(resultado, 2))

assert original == "".join(antes)
assert modificado == "".join(depois)

Essa restauração vale para o formato Differ/ndiff, não para qualquer diff unificado arbitrário.

Differ para comparação linha a linha

A classe Differ oferece a mesma família de saída e permite filtros de linha e caractere.

from difflib import Differ

comparador = Differ()
resultado = comparador.compare(antes, depois)
print("".join(resultado))

O resultado não pretende ser o patch mínimo. Correspondências locais geralmente são mais legíveis que sincronizações distantes e acidentais.

Relatório lado a lado com HtmlDiff

HtmlDiff gera uma tabela ou documento completo com mudanças entre linhas e dentro das linhas.

from difflib import HtmlDiff

html = HtmlDiff(wrapcolumn=80).make_file(
    antes,
    depois,
    fromdesc="Versão anterior",
    todesc="Versão nova",
    context=True,
    numlines=3,
)

with open("comparacao.html", "w", encoding="utf-8") as arquivo:
    arquivo.write(html)

Esse formato é útil em revisão editorial e relatórios de auditoria.

Segurança no HtmlDiff

fromdesc e todesc são interpretados como HTML sem escape. Se vierem de usuários, aplique html.escape().

from html import escape

titulo = escape(nome_enviado_pelo_usuario)

Trate também o arquivo gerado como conteúdo potencialmente sensível, pois ele inclui trechos dos documentos comparados.

Comparando bytes

diff_bytes() ajuda quando a codificação é desconhecida ou inconsistente.

from difflib import diff_bytes, unified_diff

a = [b"linha antiga\n"]
b = [b"linha nova\n"]

resultado = diff_bytes(unified_diff, a, b)
print(b"".join(resultado))

A função converte internamente sem perda e devolve bytes. Para texto com codificação conhecida, decodificar corretamente continua sendo preferível.

difflib versus filecmp

difflib explica diferenças de conteúdo. O módulo filecmp responde se arquivos ou árvores parecem iguais e pode usar metadados para uma comparação superficial.

Use filecmp.cmp(..., shallow=False) para confirmar conteúdo e, quando houver divergência textual, gere um relatório com difflib. Para diretórios, dircmp identifica arquivos comuns, exclusivos e diferentes.

Normalização antes da comparação

Dependendo do objetivo, normalize Unicode, finais de linha, espaços, caixa e campos voláteis.

import unicodedata


def normalizar(texto: str) -> str:
    texto = unicodedata.normalize("NFC", texto)
    return "\n".join(linha.rstrip() for linha in texto.splitlines())

Normalizar demais pode esconder mudanças importantes. Mantenha duas modalidades: comparação estrita e comparação tolerante.

Comparando JSON e dados estruturados

Comparar JSON bruto pode marcar apenas mudanças de indentação ou ordem de chaves. Faça parsing, serialize com ordenação estável e compare a saída normalizada.

import json

normalizado = json.dumps(objeto, sort_keys=True, indent=2, ensure_ascii=False)

Para diferenças semânticas profundas, uma ferramenta específica de estrutura pode ser melhor.

Desempenho e limites

SequenceMatcher pode ter custo quadrático no pior caso. Arquivos enormes ou sequências altamente repetitivas exigem limites de tamanho, tempo e memória.

Não envie documentos ilimitados para uma comparação síncrona de API. Considere streaming, amostragem, ferramentas externas especializadas ou filas de trabalho.

Testando um comparador

def similar(a: str, b: str, limite: float = 0.8) -> bool:
    return SequenceMatcher(None, a, b).ratio() >= limite

assert similar("instalar", "instlar")
assert not similar("instalar", "remover")

Crie um conjunto rotulado com casos reais para calibrar o limite. Teste textos vazios, Unicode, repetição, espaços e inversão dos argumentos.

Erros frequentes

  • Tratar ratio() como probabilidade.
  • Usar difflib como busca semântica.
  • Ignorar a influência da ordem dos argumentos.
  • Comparar arquivos sem preservar finais de linha.
  • Exibir descrições não escapadas em HtmlDiff.
  • Desativar autojunk sem medir desempenho.
  • Carregar arquivos gigantes sem limite.
  • Esperar que o diff seja uma sequência mínima de edições.

Boas práticas

  • Escolha a granularidade: caractere, token ou linha.
  • Normalize apenas diferenças irrelevantes ao domínio.
  • Calibre cortes com exemplos reais.
  • Use diffs unificados para ferramentas e HTML para pessoas.
  • Escape metadados inseridos no HTML.
  • Defina limites de tamanho e tempo.
  • Use filecmp antes quando basta saber se arquivos são iguais.
  • Teste Unicode, whitespace e sequências repetitivas.

Conclusão

O módulo difflib no Python transforma comparação de sequências em relatórios úteis para pessoas e programas. SequenceMatcher oferece blocos, operações e índices de similaridade; get_close_matches() sugere alternativas; e as funções de diff produzem formatos unificado, contextual, detalhado e HTML.

O resultado melhora quando a aplicação define claramente o que é uma mudança relevante, escolhe a granularidade correta e limita entradas grandes. Com normalização controlada, segurança no HTML e testes de limiar, difflib serve como base para revisores, validadores, CLIs e ferramentas de auditoria sem depender de um sistema externo de diff.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Painel de gráficos representando análise estatística de dados no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    statistics no Python: análise de dados

    Aprenda statistics no Python para média, mediana, desvio padrão, quantis, correlação, regressão, NormalDist e KDE.

    Ler mais

    Tempo de leitura: 8 minutos
    31/07/2026
    Gráficos de frações representando números racionais exatos no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    fractions no Python: números racionais

    Aprenda fractions no Python para cálculos racionais exatos, simplificação, limit_denominator, formatação e conversões seguras.

    Ler mais

    Tempo de leitura: 7 minutos
    31/07/2026
    Calculadora e documentos representando cálculos decimais precisos no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    Decimal no Python: cálculos precisos

    Aprenda Decimal no Python para cálculos exatos, dinheiro, quantize, arredondamento, contextos e validação sem erros de float.

    Ler mais

    Tempo de leitura: 8 minutos
    30/07/2026
    Código digital representando identificadores UUID únicos e ordenáveis no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    uuid no Python: IDs únicos e ordenáveis

    Aprenda uuid no Python: versões 4, 5, 6 e 7, validação, bancos de dados, IDs ordenáveis e cuidados de segurança.

    Ler mais

    Tempo de leitura: 8 minutos
    30/07/2026
    Arquivos organizados representando armazenamento temporário seguro no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    tempfile no Python: arquivos temporários

    Aprenda tempfile no Python para criar arquivos e pastas temporárias com segurança, limpeza automática e suporte a Windows e Unix.

    Ler mais

    Tempo de leitura: 8 minutos
    29/07/2026
    Relógios representando fusos horários internacionais com zoneinfo no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    zoneinfo no Python: fusos horários

    Aprenda zoneinfo no Python para converter fusos, lidar com horário de verão, fold, UTC e tzdata sem erros de agendamento.

    Ler mais

    Tempo de leitura: 8 minutos
    29/07/2026