difflib en Python: compara textos y archivos

Publicado el: 01/08/2026
Tempo de leitura: 6 minutos
Documentos de texto que representan comparación de versiones con difflib en Python

Comparar dos versiones de un texto es útil en revisión, auditoría, migraciones, pruebas, sincronización y herramientas de línea de comandos. En lugar de descubrir solamente que los contenidos son distintos, muchas aplicaciones necesitan mostrar qué líneas fueron eliminadas, añadidas o modificadas. El módulo difflib en Python ofrece comparación de secuencias, deltas legibles, sugerencias por similitud e informes HTML sin dependencias externas.

Esta guía explica SequenceMatcher, get_close_matches(), unified_diff(), context_diff(), ndiff(), restore() y HtmlDiff. Complementa nuestros artículos sobre listas en Python, slicing, archivos temporales, collections y archivos de texto.

Qué compara difflib

difflib trabaja con secuencias cuyos elementos son hashable. Esto incluye cadenas, listas de líneas y listas de tokens.

from difflib import SequenceMatcher

anterior = "Python es simple"
nuevo = "Python es muy simple"

matcher = SequenceMatcher(None, anterior, nuevo)
print(matcher.ratio())

El algoritmo encuentra bloques contiguos comunes y aplica la misma idea recursivamente a las partes restantes. Favorece coincidencias naturales para personas, pero no promete una secuencia mínima de ediciones.

Similitud con ratio()

ratio() devuelve un valor entre cero y uno. Los valores próximos a uno indican gran similitud.

similitud = SequenceMatcher(None, "configuracion", "configuración").ratio()
print(round(similitud, 3))

El valor no es una probabilidad y no tiene un umbral universal. Calibra el corte con ejemplos reales y mide falsos positivos y negativos.

El orden de argumentos puede influir

La documentación oficial de difflib advierte que ratio() puede cambiar al invertir las secuencias.

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

Si tu regla necesita simetría, calcula ambas direcciones o utiliza una métrica diseñada para ser simétrica.

quick_ratio() y real_quick_ratio()

Estos métodos devuelven límites superiores más rápidos para la razón completa.

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

En una búsqueda grande, usa las estimaciones para descartar candidatos obvios y calcula la razón completa solo para los restantes.

Encontrar bloques coincidentes

get_matching_blocks() informa posiciones y tamaños de los bloques iguales.

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

El último bloque siempre es un centinela de tamaño cero. Esta API sirve para resaltado y visualizaciones personalizadas.

Transformaciones con get_opcodes()

get_opcodes() describe cómo transformar la primera secuencia en la 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])

Las etiquetas son equal, replace, delete e insert. La salida estructurada resulta útil para interfaces.

Comparar muchas entradas con una referencia

SequenceMatcher guarda información de la segunda secuencia. Configura una referencia una vez cuando varias entradas se comparan con el mismo catálogo.

matcher = SequenceMatcher(None)
matcher.set_seq2("configuración")

for entrada in ["configuracion", "config", "configuraciones"]:
    matcher.set_seq1(entrada)
    print(entrada, matcher.ratio())

Este reaprovechamiento reduce trabajo en correctores y normalizadores.

Sugerencias con get_close_matches()

get_close_matches() devuelve los mejores candidatos por encima de un corte.

from difflib import get_close_matches

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

Es útil en CLIs, validación y mensajes “quizás quisiste decir”. No es búsqueda semántica porque compara forma, no significado.

La heurística autojunk

En secuencias de al menos 200 elementos, la heurística automática trata elementos muy repetidos como populares y reduce su papel.

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

Desactiva autojunk cuando la repetición tiene significado, como ADN, logs estructurados o códigos. Mide el rendimiento.

Elementos junk personalizados

El primer argumento del constructor puede marcar elementos que no deben servir de anclas.

matcher = SequenceMatcher(
    lambda caracter: caracter in " \t",
    "nombre = valor",
    "nombre=valor",
)

Junk guía la coincidencia, pero no elimina diferencias. Si los espacios deben ignorarse por completo, normaliza antes y conserva originales para mostrar.

Diff unificado

unified_diff() produce el formato usado por parches y revisiones.

from difflib import unified_diff

antes = "línea 1\nlínea antigua\nlínea 3\n".splitlines(keepends=True)
despues = "línea 1\nlínea nueva\nlínea 3\n".splitlines(keepends=True)

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

La función devuelve un generador. Conviértelo en lista solo si necesitas reutilizar o contar la salida.

Conservar finales de línea

Usa splitlines(keepends=True) al comparar archivos. Las líneas de control incluyen terminadores para funcionar con writelines().

Si las entradas no tienen terminadores, pasa lineterm="".

Diff contextual

context_diff() presenta los cambios en bloques antes/después.

from difflib import context_diff

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

n controla las líneas sin cambios alrededor de cada modificación.

Salida detallada con ndiff()

ndiff() añade un prefijo a cada línea:

  • - : exclusiva de la primera;
  • + : exclusiva de la segunda;
  • : común;
  • ? : guía para cambios internos.
from difflib import ndiff

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

Las líneas ? no estaban en los originales y pueden ser confusas con tabs.

Restaurar textos con restore()

Un delta de ndiff() puede reconstruir cualquiera de las versiones.

from difflib import restore

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

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

La restauración funciona con salida Differ/ndiff, no con cualquier parche unificado.

Differ para comparar líneas

La clase Differ ofrece una salida similar y filtros opcionales.

from difflib import Differ

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

Differ no pretende generar el delta mínimo. Las coincidencias locales suelen ser más legibles que sincronizaciones distantes.

Informes lado a lado con HtmlDiff

HtmlDiff genera una tabla o documento completo con cambios entre líneas y dentro de ellas.

from difflib import HtmlDiff

html = HtmlDiff(wrapcolumn=80).make_file(
    antes,
    despues,
    fromdesc="Versión anterior",
    todesc="Versión nueva",
    context=True,
    numlines=3,
)

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

Es útil en revisión editorial y auditoría.

Seguridad de HtmlDiff

fromdesc y todesc se interpretan como HTML sin escapar. Escapa valores del usuario.

from html import escape

titulo = escape(nombre_del_usuario)

El informe contiene fragmentos de documentos y puede ser sensible.

Comparar bytes

diff_bytes() ayuda cuando la codificación es desconocida o inconsistente.

from difflib import diff_bytes, unified_diff

a = [b"linea antigua\n"]
b = [b"linea nueva\n"]

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

Con una codificación conocida, decodificar correctamente sigue siendo mejor.

difflib frente a filecmp

difflib explica diferencias de contenido. El módulo filecmp determina si archivos o árboles parecen iguales y puede usar metadatos.

Usa filecmp.cmp(..., shallow=False) para confirmar igualdad de contenido y genera un informe textual con difflib cuando haya diferencias. dircmp identifica archivos comunes, exclusivos y distintos.

Normalización previa

Según el objetivo, normaliza Unicode, finales de línea, espacios, mayúsculas y campos variables.

import unicodedata


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

Normalizar demasiado puede ocultar cambios importantes. Ofrece modos estricto y tolerante.

Comparar JSON y datos estructurados

Comparar JSON crudo puede marcar solo indentación u orden de claves. Haz parsing, serializa con orden estable y compara la salida.

import json

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

Para diferencias semánticas profundas, una herramienta de estructuras puede ser mejor.

Rendimiento y límites

SequenceMatcher puede tener coste cuadrático en el peor caso. Archivos enormes y secuencias repetitivas necesitan límites de tamaño, memoria y tiempo.

No aceptes documentos ilimitados en una API síncrona. Considera herramientas especializadas o trabajos en cola para entradas grandes.

Probar una regla de similitud

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", "eliminar")

Construye un conjunto rotulado real para calibrar el límite. Prueba cadenas vacías, Unicode, repetición, espacios e inversión de argumentos.

Errores frecuentes

  • Tratar ratio() como probabilidad.
  • Usar difflib como búsqueda semántica.
  • Ignorar el orden de argumentos.
  • Comparar archivos sin conservar finales de línea.
  • Mostrar descripciones sin escape en HtmlDiff.
  • Desactivar autojunk sin medir.
  • Cargar archivos ilimitados.
  • Esperar una edición mínima.

Buenas prácticas

  • Elige granularidad de carácter, token o línea.
  • Normaliza solo diferencias irrelevantes.
  • Calibra cortes con datos reales.
  • Usa diffs unificados para herramientas y HTML para personas.
  • Escapa metadatos del HTML.
  • Define límites de tamaño y tiempo.
  • Usa filecmp primero cuando basta igualdad.
  • Prueba Unicode, espacios y repetición.

Conclusión

El módulo difflib en Python transforma la comparación de secuencias en informes útiles para personas y programas. SequenceMatcher ofrece bloques, operaciones y puntuaciones; get_close_matches() sugiere alternativas; y las funciones de diff producen formatos unificado, contextual, detallado y HTML.

El resultado mejora cuando la aplicación define qué cambio importa, elige la granularidad correcta y limita entradas grandes. Con normalización controlada, HTML seguro y pruebas de umbral, difflib sirve de base para revisores, validadores, CLIs y herramientas de auditoría.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Panel de gráficos que representa análisis estadístico de datos en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    statistics en Python: análisis de datos

    Aprende statistics en Python para media, mediana, desviación, cuantiles, correlación, regresión, NormalDist y KDE.

    Ler mais

    Tempo de leitura: 7 minutos
    31/07/2026
    Gráficos de fracciones que representan números racionales exactos en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    fractions en Python: números racionales

    Aprende fractions en Python para aritmética racional exacta, reducción automática, limit_denominator, formato y conversiones seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    31/07/2026
    Calculadora y documentos que representan cálculos Decimal precisos en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Decimal en Python: cálculos precisos

    Aprende Decimal en Python para cálculos exactos, dinero, quantize, redondeo, contextos y validación sin errores de float.

    Ler mais

    Tempo de leitura: 7 minutos
    30/07/2026
    Código digital que representa identificadores UUID únicos y ordenables en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    uuid en Python: IDs únicos y ordenables

    Aprende uuid en Python: versiones 4, 5, 6 y 7, validación, almacenamiento, IDs ordenables y buenas prácticas de seguridad.

    Ler mais

    Tempo de leitura: 7 minutos
    30/07/2026
    Archivos organizados que representan almacenamiento temporal seguro en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    tempfile en Python: archivos temporales

    Aprende tempfile en Python para crear archivos y carpetas temporales seguros, con limpieza automática en Windows y Unix.

    Ler mais

    Tempo de leitura: 8 minutos
    29/07/2026
    Relojes que representan zonas horarias internacionales con zoneinfo en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    zoneinfo en Python: zonas horarias

    Aprende zoneinfo en Python para convertir zonas, tratar horario de verano, fold, UTC y tzdata sin errores de programación.

    Ler mais

    Tempo de leitura: 7 minutos
    29/07/2026