2009-04-21 4 views
19

Beim Vergleich ähnliche Zeilen möchte ich die Unterschiede auf der gleichen Linie markieren:Python-Diffib: Unterschiede inline hervorheben?

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

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

Während difflib.HtmlDiff diese Art von Inline-Markierung zu tun scheint, produziert sie sehr ausführlich Markup.

Leider konnte ich keine andere Klasse/Methode finden, die nicht Zeile für Zeile funktioniert.

Fehle ich etwas? Irgendwelche Hinweise würden geschätzt!

Antwort

35

Für Ihr einfaches Beispiel:

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' 

Dies funktioniert mit Streichern. Sie sollten entscheiden, was mit "Ersetzen" Opcodes zu tun ist.

+0

Vielen Dank dafür! Das ist genau die Art von Probe, die ich brauchte. Ich hatte keine Ahnung, wie ich anfangen soll, aber das illustriert es sehr gut. Nochmals vielen Dank! – AnC

+0

+1 Danke für Ihr Beispiel :) Was würden Sie vorschlagen, um optcodes zu ersetzen? – Viet

+0

Nun, ein Vorschlag wäre, einige 'Replace'-Opcodes in freier Wildbahn zu entdecken; Die Dokumentation sagt, dass sie produziert werden können, aber ich kann mich nicht erinnern, jemals etwas gesehen zu haben (IIRC Ich habe nur 'delete's' gefolgt von 'insert's' gesehen). In jedem Fall, was mit "Ersetzen" zu tun ist, ist dem OP überlassen. – tzot

2

difflib.SequenceMatcher wird auf einzelnen Zeilen arbeiten. Mit den Opcodes können Sie festlegen, wie die erste Zeile in die zweite Zeile geändert wird.

+1

Ich fürchte, ich nicht ganz verstehen - doch sowieso, also werde ich mehr zu graben tun. Danke. – AnC

+0

Was genau versuchen Sie mit den Unterschieden zu tun? Möchten Sie eine HTML-Ausgabe erstellen oder verwenden Sie nur HtmlDiff, da es in-line diffing ist? – Adam

+0

Während die HTML-Ausgabe mein primärer Anwendungsfall ist, erlaubt die Ausgabe von HtmlDiff keine einfache Wiederverwendung - das heißt, wenn sie einfach INS und DEL einfügen würde, könnte sie dann leicht in das weiter unten benötigte umgewandelt werden. – AnC

0

Hier ist ein Inline-answer above

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()) 

Es ist nicht perfekt (auch 3 kompatibel Python) durch @ tzot inspiriert unterscheiden - zum Beispiel, wäre es schön, zu erweitern ‚ersetzen‘ Opcodes das volle Wort stattdessen ersetzt zu erkennen von den wenigen verschiedenen Buchstaben, aber es ist ein guter Anfang.

Beispielausgabe:

>>> 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 
Verwandte Themen