2009-04-21 8 views
19

Lorsque l'on compare les lignes similaires, je veux mettre en évidence les différences sur la même ligne:Python difflib: mettre en évidence les différences en ligne?

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

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

Alors que difflib.HtmlDiff semble faire ce genre de mise en évidence en ligne, il produit un balisage très bavard.

Malheureusement, je n'ai pas réussi à trouver une autre classe/méthode qui ne fonctionne pas ligne par ligne.

Est-ce que je manque quelque chose? Des pointeurs seraient appréciés!

Répondre

35

Pour votre exemple simple:

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' 

Cela fonctionne avec des cordes. Vous devriez décider quoi faire avec "remplacer" les opcodes.

+0

Merci beaucoup pour ça! C'est exactement le genre d'échantillon dont j'avais besoin. Je n'avais aucune idée de comment commencer, mais cela illustre très bien. Encore une fois, merci beaucoup! – AnC

+0

+ 1 merci pour votre exemple :) Que suggérez-vous de faire avec le remplacement des optcodes? – Viet

+0

Eh bien, une suggestion serait de découvrir certains opcodes "remplacer" dans la nature; la documentation dit qu'ils peuvent être produits, mais je ne me souviens pas d'avoir jamais vu (IIRC j'ai seulement vu 'delete' suivi de 'insert's). En tout cas, que faire avec 'remplacer est jusqu'à l'OP. – tzot

2

difflib.SequenceMatcher fonctionnera sur les lignes simples. Vous pouvez utiliser les "opcodes" pour déterminer comment changer la première ligne pour en faire la deuxième ligne.

+1

Je crains que je ne comprends pas tout à fait cela - encore en tout cas, je vais donc faire creuser davantage. Merci. – AnC

+0

Qu'essayez-vous exactement de faire avec les différences? Voulez-vous une sortie HTML ou utilisiez-vous simplement le HtmlDiff parce qu'il différait en ligne? – Adam

+0

Alors que la sortie HTML est mon principal cas d'utilisation, la sortie de HtmlDiff ne permet pas une réutilisation facile, c'est-à-dire si elle insérait simplement INS et DEL, elle pourrait facilement être transformée en tout ce qui est nécessaire. – AnC

0

est ici une ligne diffèrent inspiré par answer above de @ tzot (aussi 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()) 

Ce n'est pas parfait - par exemple, il serait bien d'élargir « remplacer » opcodes pour reconnaître le mot entier remplacé à la place de quelques lettres différentes, mais c'est un bon point de départ.

Exemple de sortie:

>>> 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 
Questions connexes