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

    Documento y bandeja de entrada que representan buzones de correo con mailbox en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    mailbox en Python: buzones de correo

    Aprende mailbox en Python para leer, crear y migrar Maildir, mbox y MH con locking, flags, mensajes y manejo seguro

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Editor de texto que representa formato con textwrap en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    textwrap en Python: formatea textos

    Aprende textwrap en Python para dividir, rellenar, acortar, indentar y quitar sangrías con control de ancho, espacios y palabras largas.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Carpeta y lupa que representan filtros de nombres con fnmatch en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    fnmatch en Python: filtra nombres de archivos

    Aprende fnmatch en Python para filtrar nombres de archivos con comodines, controlar mayúsculas, excluir patrones y distinguir glob de regex.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Monitor con datos binarios que representa arrays numéricos compactos en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    array en Python: números compactos

    Aprende array en Python para almacenar números compactos, usar archivos binarios, byte order, memoryview y buffers seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Círculo cromático que representa conversiones RGB, HSV y HLS con colorsys en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    colorsys en Python: RGB, HSV y HLS

    Aprende colorsys en Python para convertir colores entre RGB, HSV, HLS y YIQ, crear paletas y evitar errores de escala

    Ler mais

    Tempo de leitura: 5 minutos
    09/08/2026
    Icono de configuración que representa archivos plist con plistlib en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    plistlib: lee y escribe archivos plist

    Aprende plistlib en Python para leer y escribir archivos plist XML y binarios, validar datos y manejar fechas, bytes y

    Ler mais

    Tempo de leitura: 6 minutos
    08/08/2026