2009-04-21 6 views
19

Al comparar líneas similares, quiero poner de relieve las diferencias en la misma línea:Diflib de Python: resaltando las diferencias en línea?

a) lorem ipsum dolor sit amet 
b) lorem foo ipsum dolor amet 

lorem <ins>foo</ins> ipsum dolor <del>sit</del> amet 

Mientras difflib.HtmlDiff parece hacer este tipo de resaltado de línea, que produce marcado muy ampliada.

Desafortunadamente, no he podido encontrar otra clase/método que no funciona línea por línea.

¿E-mail perdiendo cualquier cosa? ¡Cualquier puntero sería apreciado!

Respuesta

35

Para su ejemplo sencillo:

import difflib 
def show_diff(seqm): 
    """Unify operations between two compared strings 
seqm is a difflib.SequenceMatcher instance whose a & b are strings""" 
    output= [] 
    for opcode, a0, a1, b0, b1 in seqm.get_opcodes(): 
     if opcode == 'equal': 
      output.append(seqm.a[a0:a1]) 
     elif opcode == 'insert': 
      output.append("<ins>" + seqm.b[b0:b1] + "</ins>") 
     elif opcode == 'delete': 
      output.append("<del>" + seqm.a[a0:a1] + "</del>") 
     elif opcode == 'replace': 
      raise NotImplementedError, "what to do with 'replace' opcode?" 
     else: 
      raise RuntimeError, "unexpected opcode" 
    return ''.join(output) 

>>> sm= difflib.SequenceMatcher(None, "lorem ipsum dolor sit amet", "lorem foo ipsum dolor amet") 
>>> show_diff(sm) 
'lorem<ins> foo</ins> ipsum dolor <del>sit </del>amet' 

Esto funciona con cuerdas. Debería decidir qué hacer con los códigos de operación "reemplazar".

+0

¡Muchas gracias por esto! Ese es exactamente el tipo de muestra que necesitaba. No tenía idea de cómo empezar, pero esto lo ilustra muy bien. ¡De nuevo, muchas gracias! – AnC

+0

+1 gracias por su ejemplo :) ¿Qué sugieres que hagas con reemplazar optcodes? – Viet

+0

Bueno, una sugerencia sería descubrir algunos códigos de operación 'reemplazar' en la naturaleza; la documentación dice que pueden ser producidos, pero no recuerdo haber visto ninguno (IIRC solo he visto 'borrar' seguido de 'inserto'). En cualquier caso, qué hacer con 'replace's depende del OP. – tzot

2

difflib.SequenceMatcher operará en líneas únicas. Puede usar los "códigos de operación" para determinar cómo cambiar la primera línea para convertirla en la segunda línea.

+1

Me temo que no entiendo muy bien esto - sin embargo, de todos modos, por lo que voy a hacer más excavaciones. Gracias. – AnC

+0

¿Qué estás tratando de hacer exactamente con las diferencias? ¿Desea salida de HTML o simplemente estaba usando HtmlDiff porque difería en línea? – Adam

+0

Si bien la salida HTML es mi caso de uso principal, la salida de HtmlDiff no permite una fácil reutilización, es decir, si simplemente insertara INS y DEL, eso podría transformarse fácilmente en lo que sea necesario más adelante. – AnC

0

He aquí una línea difieren inspirado por @ de tzot answer above (también Python 3 compatible)

def inline_diff(a, b): 
    import difflib 
    matcher = difflib.SequenceMatcher(None, a, b) 
    def process_tag(tag, i1, i2, j1, j2): 
     if tag == 'replace': 
      return '{' + matcher.a[i1:i2] + ' -> ' + matcher.b[j1:j2] + '}' 
     if tag == 'delete': 
      return '{- ' + matcher.a[i1:i2] + '}' 
     if tag == 'equal': 
      return matcher.a[i1:i2] 
     if tag == 'insert': 
      return '{+ ' + matcher.b[j1:j2] + '}' 
     assert false, "Unknown tag %r"%tag 
    return ''.join(process_tag(*t) for t in matcher.get_opcodes()) 

No es perfecto - por ejemplo, que sería bueno para expandir 'reemplazar' códigos de operación para reconocer la palabra completa reemplazado en su lugar de las pocas letras diferentes, pero es un buen lugar para comenzar.

Salida de ejemplo:

>>> a='Lorem ipsum dolor sit amet consectetur adipiscing' 
>>> b='Lorem bananas ipsum cabbage sit amet adipiscing' 
>>> print(inline_diff(a, b)) 
Lorem{+ bananas} ipsum {dolor -> cabbage} sit amet{- consectetur} adipiscing 
Cuestiones relacionadas