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

    Documento e caixa de entrada representando caixas de e-mail com mailbox no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    mailbox no Python: caixas de e-mail

    Aprenda mailbox no Python para ler, criar e migrar caixas Maildir, mbox e MH com locking, mensagens, flags e tratamento

    Ler mais

    Tempo de leitura: 6 minutos
    12/08/2026
    Editor de texto representando formatação com textwrap no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    textwrap no Python: formate textos

    Aprenda textwrap no Python para quebrar, preencher, encurtar, indentar e remover recuos de textos com controle de largura e espaços.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Pasta e lupa representando filtros de nomes com fnmatch no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    fnmatch no Python: filtre nomes de arquivos

    Aprenda fnmatch no Python para filtrar nomes de arquivos com curingas, controlar maiúsculas, excluir padrões e evitar confundir glob com

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Monitor com dados binários representando arrays numéricos compactos no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    array no Python: números compactos

    Aprenda array no Python para armazenar números compactos, manipular bytes, arquivos binários e buffers com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    10/08/2026
    Círculo cromático representando conversões RGB, HSV e HLS com colorsys no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    colorsys no Python: RGB, HSV e HLS

    Aprenda colorsys no Python para converter cores entre RGB, HSV, HLS e YIQ, gerar paletas e evitar erros com escalas

    Ler mais

    Tempo de leitura: 6 minutos
    09/08/2026
    Ícone de configuração representando arquivos plist com plistlib no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    plistlib no Python: arquivos plist

    Aprenda plistlib no Python para ler e gravar arquivos plist XML e binários, validar dados e integrar configurações Apple com

    Ler mais

    Tempo de leitura: 7 minutos
    08/08/2026