difflib — Hilfsprogramme zur Berechnung von Differenzen¶
Quellcode: Lib/difflib.py
Dieses Modul stellt Klassen und Funktionen zum Vergleichen von Sequenzen bereit. Es kann beispielsweise zum Vergleichen von Dateien verwendet werden und kann Informationen über Dateidifferenzen in verschiedenen Formaten liefern, einschließlich HTML und Kontext- und Unified-Diffs. Zum Vergleichen von Verzeichnissen und Dateien siehe auch das Modul filecmp.
- class difflib.SequenceMatcher
Dies ist eine flexible Klasse zum Vergleichen von Paaren von Sequenzen beliebigen Typs, solange die Sequenzelemente hashbar sind. Der grundlegende Algorithmus ist älter als ein Algorithmus, der Ende der 1980er Jahre von Ratcliff und Obershelp unter dem hyperbolischen Namen „gestalt pattern matching“ veröffentlicht wurde, und ein wenig ausgefeilter. Die Idee ist, die längste zusammenhängende übereinstimmende Untersequenz zu finden, die keine „Junk“-Elemente enthält; diese „Junk“-Elemente sind solche, die in gewisser Weise uninteressant sind, wie z. B. Leerzeilen oder Leerzeichen. (Die Behandlung von Junk ist eine Erweiterung des Ratcliff- und Obershelp-Algorithmus.) Dieselbe Idee wird dann rekursiv auf die Teile der Sequenzen links und rechts der übereinstimmenden Untersequenz angewendet. Dies liefert keine minimalen Editiersequenzen, führt aber tendenziell zu Übereinstimmungen, die für Menschen „richtig aussehen“.
Timing: Der grundlegende Ratcliff-Obershelp-Algorithmus hat im Worst Case eine kubische Zeitkomplexität und im erwarteten Fall eine quadratische Zeitkomplexität.
SequenceMatcherhat im Worst Case eine quadratische Zeitkomplexität und ein erwartetes Verhalten, das auf komplizierte Weise davon abhängt, wie viele Elemente die Sequenzen gemeinsam haben; die Best-Case-Zeit ist linear.Automatische Junk-Heuristik:
SequenceMatcherunterstützt eine Heuristik, die bestimmte Sequenzelemente automatisch als Junk behandelt. Die Heuristik zählt, wie oft jedes einzelne Element in der Sequenz vorkommt. Wenn Duplikate eines Elements (nach dem ersten) mehr als 1 % der Sequenz ausmachen und die Sequenz mindestens 200 Elemente lang ist, wird dieses Element als „populär“ markiert und für den Sequenzvergleich als Junk behandelt. Diese Heuristik kann deaktiviert werden, indem das Argumentautojunkbeim Erstellen desSequenceMatcheraufFalsegesetzt wird.Geändert in Version 3.2: Parameter autojunk hinzugefügt.
- class difflib.Differ¶
Dies ist eine Klasse zum Vergleichen von Textzeilensequenzen und zur Erzeugung von menschenlesbaren Differenzen oder Deltas. Differ verwendet
SequenceMatchersowohl zum Vergleichen von Zeilensequenzen als auch zum Vergleichen von Zeichensequenzen innerhalb ähnlicher (nahezu übereinstimmender) Zeilen.Jede Zeile eines
Differ-Deltas beginnt mit einem zweibuchstabigen CodeCode
Bedeutung
'- 'nur in Sequenz 1 vorhandene Zeile
'+ 'nur in Sequenz 2 vorhandene Zeile
' 'gemeinsame Zeile beider Sequenzen
'? 'in keiner der Eingabesequenzen vorhandene Zeile
Zeilen, die mit „
?“ beginnen, versuchen, das Auge auf Zeilenunterschiede zu lenken und waren in keiner der Eingabesequenzen vorhanden. Diese Zeilen können verwirrend sein, wenn die Sequenzen Leerzeichen wie Leerzeichen, Tabs oder Zeilenumbrüche enthalten.
- class difflib.HtmlDiff¶
Diese Klasse kann verwendet werden, um eine HTML-Tabelle (oder eine vollständige HTML-Datei, die die Tabelle enthält) zu erstellen, die einen nebeneinander liegenden, zeilenweisen Vergleich von Text mit Hervorhebung von Zeilen- und Zeilenänderungen anzeigt. Die Tabelle kann entweder im vollständigen oder im kontextuellen Differenzmodus generiert werden.
Der Konstruktor für diese Klasse ist
- __init__(tabsize=8, wrapcolumn=None, linejunk=None, charjunk=IS_CHARACTER_JUNK)¶
Initialisiert eine Instanz von
HtmlDiff.tabsize ist ein optionales Schlüsselwortargument zur Angabe des Tabulatorabstands und hat den Standardwert
8.wrapcolumn ist ein optionales Schlüsselwort zur Angabe der Spaltennummer, bei der Zeilen umgebrochen und umgebrochen werden; der Standardwert ist
None, was bedeutet, dass Zeilen nicht umgebrochen werden.linejunk und charjunk sind optionale Schlüsselwortargumente, die an
ndiff()übergeben werden (vonHtmlDiffverwendet, um die nebeneinander liegenden HTML-Differenzen zu generieren). Siehe die Dokumentation zundiff()für Standardwerte und Beschreibungen der Argumente.
Die folgenden Methoden sind öffentlich
- make_file(fromlines, tolines, fromdesc='', todesc='', context=False, numlines=5, *, charset='utf-8')¶
Vergleicht fromlines und tolines (Listen von Zeichenketten) und gibt eine Zeichenkette zurück, die eine vollständige HTML-Datei mit einer Tabelle ist, die zeilenweise Unterschiede mit hervorgehobenen Zeilen- und Zeilenänderungen zeigt.
fromdesc und todesc sind optionale Schlüsselwortargumente zur Angabe von Spaltenüberschriften für die von-/zu-Dateien (beide sind standardmäßig leer).
context und numlines sind beides optionale Schlüsselwortargumente. Setzen Sie context auf
True, wenn kontextuelle Unterschiede angezeigt werden sollen, andernfalls ist der StandardwertFalse, um die vollständigen Dateien anzuzeigen. numlines hat den Standardwert5. Wenn contextTrueist, steuert numlines die Anzahl der Kontextzeilen, die die Differenzhervorhebungen umgeben. Wenn contextFalseist, steuert numlines die Anzahl der Zeilen, die vor einer Differenzhervorhebung angezeigt werden, wenn die „nächsten“-Hyperlinks verwendet werden (ein Wert von Null würde dazu führen, dass die „nächsten“-Hyperlinks die nächste Differenzhervorhebung am oberen Rand des Browsers ohne führenden Kontext platzieren).Hinweis
fromdesc und todesc werden als unescaped HTML interpretiert und sollten ordnungsgemäß escaped werden, wenn Eingaben von nicht vertrauenswürdigen Quellen empfangen werden.
Geändert in Version 3.5: Das Schlüsselwortargument charset wurde hinzugefügt. Der Standard-Charset des HTML-Dokuments änderte sich von
'ISO-8859-1'zu'utf-8'.
- make_table(fromlines, tolines, fromdesc='', todesc='', context=False, numlines=5)¶
Vergleicht fromlines und tolines (Listen von Zeichenketten) und gibt eine Zeichenkette zurück, die eine vollständige HTML-Tabelle ist, die zeilenweise Unterschiede mit hervorgehobenen Zeilen- und Zeilenänderungen zeigt.
Die Argumente für diese Methode sind die gleichen wie für die Methode
make_file().
- difflib.context_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n')¶
Vergleicht a und b (Listen von Zeichenketten); gibt ein Delta zurück (ein Generator, der die Deltazeilen generiert) im Kontext-Diff-Format.
Kontext-Diffs sind eine kompakte Möglichkeit, nur die geänderten Zeilen plus einige Kontextzeilen anzuzeigen. Die Änderungen werden im Vorher/Nachher-Stil dargestellt. Die Anzahl der Kontextzeilen wird durch n bestimmt, das standardmäßig drei ist.
Standardmäßig werden die Diff-Steuerzeilen (die mit
***oder---) mit einem nachfolgenden Zeilenumbruch erstellt. Dies ist hilfreich, damit Eingaben, die ausio.IOBase.readlines()erstellt wurden, zu Diffs führen, die für die Verwendung mitio.IOBase.writelines()geeignet sind, da sowohl die Eingaben als auch die Ausgaben nachfolgende Zeilenumbrüche haben.Für Eingaben, die keine nachfolgenden Zeilenumbrüche haben, setzen Sie das Argument lineterm auf
"", damit die Ausgabe einheitlich zeilenumbruchfrei ist.Das Kontext-Diff-Format enthält normalerweise eine Kopfzeile für Dateinamen und Änderungszeiten. Beliebige oder alle dieser können mit Zeichenketten für fromfile, tofile, fromfiledate und tofiledate angegeben werden. Die Änderungszeiten werden normalerweise im ISO 8601-Format ausgedrückt. Wenn sie nicht angegeben werden, sind die Zeichenketten standardmäßig leer.
>>> import sys >>> from difflib import * >>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n'] >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n'] >>> sys.stdout.writelines(context_diff(s1, s2, fromfile='before.py', ... tofile='after.py')) *** before.py --- after.py *************** *** 1,4 **** ! bacon ! eggs ! ham guido --- 1,4 ---- ! python ! eggy ! hamster guido
Siehe Eine Befehlszeilenschnittstelle für difflib für ein detaillierteres Beispiel.
- difflib.get_close_matches(word, possibilities, n=3, cutoff=0.6)¶
Gibt eine Liste der besten „gut genug“ Übereinstimmungen zurück. word ist eine Sequenz, für die nahe Übereinstimmungen gesucht werden (typischerweise eine Zeichenkette), und possibilities ist eine Liste von Sequenzen, mit denen word abgeglichen werden soll (typischerweise eine Liste von Zeichenketten).
Das optionale Argument n (Standard
3) ist die maximale Anzahl von nahen Übereinstimmungen, die zurückgegeben werden sollen; n muss größer als0sein.Das optionale Argument cutoff (Standard
0.6) ist eine Gleitkommazahl im Bereich [0, 1]. Möglichkeiten, die nicht mindestens diesen Grad an Ähnlichkeit mit word aufweisen, werden ignoriert.Die besten (höchstens n) Übereinstimmungen unter den Möglichkeiten werden in einer Liste zurückgegeben, sortiert nach Ähnlichkeitsbewertung, am ähnlichsten zuerst.
>>> get_close_matches('appel', ['ape', 'apple', 'peach', 'puppy']) ['apple', 'ape'] >>> import keyword >>> get_close_matches('wheel', keyword.kwlist) ['while'] >>> get_close_matches('pineapple', keyword.kwlist) [] >>> get_close_matches('accept', keyword.kwlist) ['except']
- difflib.ndiff(a, b, linejunk=None, charjunk=IS_CHARACTER_JUNK)¶
Vergleicht a und b (Listen von Zeichenketten); gibt ein Delta im
Differ-Stil zurück (ein Generator, der die Deltazeilen generiert).Optionale Schlüsselwortparameter linejunk und charjunk sind Filterfunktionen (oder
None)linejunk: Eine Funktion, die ein einzelnes Zeichenkettenargument akzeptiert und
Truezurückgibt, wenn die Zeile Junk ist, oderFalse, wenn nicht. Der Standardwert istNone. Es gibt auch eine Modul-FunktionIS_LINE_JUNK(), die Zeilen ohne sichtbare Zeichen filtert, mit Ausnahme von höchstens einem Hash-Zeichen ('#') – die zugrunde liegende KlasseSequenceMatcherführt jedoch eine dynamische Analyse durch, welche Zeilen so häufig sind, dass sie Rauschen darstellen, und dies funktioniert normalerweise besser als die Verwendung dieser Funktion.charjunk: Eine Funktion, die ein Zeichen (eine Zeichenkette der Länge 1) akzeptiert und zurückgibt, ob das Zeichen Junk ist oder nicht. Der Standardwert ist die Modul-Funktion
IS_CHARACTER_JUNK(), die Leerzeichen filtert (ein Leerzeichen oder Tabulator; es ist keine gute Idee, hier einen Zeilenumbruch einzubeziehen!).>>> diff = ndiff('one\ntwo\nthree\n'.splitlines(keepends=True), ... 'ore\ntree\nemu\n'.splitlines(keepends=True)) >>> print(''.join(diff), end="") - one ? ^ + ore ? ^ - two - three ? - + tree + emu
- difflib.restore(sequence, which)¶
Gibt eine der beiden Sequenzen zurück, die ein Delta erzeugt haben.
Extrahieren Sie aus einer von
Differ.compare()oderndiff()erzeugten sequence Zeilen, die aus Datei 1 oder 2 stammen (Parameter which), und entfernen Sie die Zeilenpräfixe.Beispiel
>>> diff = ndiff('one\ntwo\nthree\n'.splitlines(keepends=True), ... 'ore\ntree\nemu\n'.splitlines(keepends=True)) >>> diff = list(diff) # materialize the generated delta into a list >>> print(''.join(restore(diff, 1)), end="") one two three >>> print(''.join(restore(diff, 2)), end="") ore tree emu
- difflib.unified_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n')¶
Vergleicht a und b (Listen von Zeichenketten); gibt ein Delta zurück (ein Generator, der die Deltazeilen generiert) im Unified-Diff-Format.
Unified-Diffs sind eine kompakte Möglichkeit, nur die geänderten Zeilen plus einige Kontextzeilen anzuzeigen. Die Änderungen werden im Inline-Stil dargestellt (anstelle von separaten Vorher/Nachher-Blöcken). Die Anzahl der Kontextzeilen wird durch n bestimmt, das standardmäßig drei ist.
Standardmäßig werden die Diff-Steuerzeilen (die mit
---,+++oder@@) mit einem nachfolgenden Zeilenumbruch erstellt. Dies ist hilfreich, damit Eingaben, die ausio.IOBase.readlines()erstellt wurden, zu Diffs führen, die für die Verwendung mitio.IOBase.writelines()geeignet sind, da sowohl die Eingaben als auch die Ausgaben nachfolgende Zeilenumbrüche haben.Für Eingaben, die keine nachfolgenden Zeilenumbrüche haben, setzen Sie das Argument lineterm auf
"", damit die Ausgabe einheitlich zeilenumbruchfrei ist.Das Unified-Diff-Format enthält normalerweise eine Kopfzeile für Dateinamen und Änderungszeiten. Beliebige oder alle dieser können mit Zeichenketten für fromfile, tofile, fromfiledate und tofiledate angegeben werden. Die Änderungszeiten werden normalerweise im ISO 8601-Format ausgedrückt. Wenn sie nicht angegeben werden, sind die Zeichenketten standardmäßig leer.
>>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n'] >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n'] >>> sys.stdout.writelines(unified_diff(s1, s2, fromfile='before.py', tofile='after.py')) --- before.py +++ after.py @@ -1,4 +1,4 @@ -bacon -eggs -ham +python +eggy +hamster guido
Siehe Eine Befehlszeilenschnittstelle für difflib für ein detaillierteres Beispiel.
- difflib.diff_bytes(dfunc, a, b, fromfile=b'', tofile=b'', fromfiledate=b'', tofiledate=b'', n=3, lineterm=b'\n')¶
Vergleicht a und b (Listen von Bytes-Objekten) mit dfunc; liefert eine Sequenz von Deltazeilen (ebenfalls Bytes) im von dfunc zurückgegebenen Format. dfunc muss aufrufbar sein, typischerweise entweder
unified_diff()odercontext_diff().Ermöglicht den Vergleich von Daten mit unbekannter oder inkonsistenter Kodierung. Alle Eingaben außer n müssen Bytes-Objekte und keine str sein. Funktioniert durch verlustfreies Konvertieren aller Eingaben (außer n) in str und Aufrufen von
dfunc(a, b, fromfile, tofile, fromfiledate, tofiledate, n, lineterm). Die Ausgabe von dfunc wird dann zurück in Bytes konvertiert, sodass die Deltazeilen, die Sie erhalten, die gleichen unbekannten/inkonsistenten Kodierungen wie a und b haben.Hinzugefügt in Version 3.5.
- difflib.IS_LINE_JUNK(line)¶
Gibt
Truefür ignorierbare Zeilen zurück. Die Zeile line ist ignorable, wenn line leer ist oder ein einzelnes'#'enthält, andernfalls ist sie nicht ignorable. Wird als Standard für den Parameter linejunk inndiff()in älteren Versionen verwendet.
- difflib.IS_CHARACTER_JUNK(ch)¶
Gibt
Truefür ignorierbare Zeichen zurück. Das Zeichen ch ist ignorable, wenn ch ein Leerzeichen oder Tabulator ist, andernfalls ist es nicht ignorable. Wird als Standard für den Parameter charjunk inndiff()verwendet.
Siehe auch
- Mustererkennung: Der Gestalt-Ansatz
Diskussion eines ähnlichen Algorithmus von John W. Ratcliff und D. E. Metzener. Dies wurde im Dr. Dobb’s Journal im Juli 1988 veröffentlicht.
SequenceMatcher-Objekte¶
Die Klasse SequenceMatcher hat diesen Konstruktor
- class difflib.SequenceMatcher(isjunk=None, a='', b='', autojunk=True)¶
Das optionale Argument isjunk muss
None(der Standardwert) oder eine einargumentige Funktion sein, die ein Sequenzelement nimmt undTruezurückgibt, wenn und nur wenn das Element „Junk“ ist und ignoriert werden sollte. Die Übergabe vonNonefür isjunk ist äquivalent zur Übergabe vonlambda x: False; anders ausgedrückt, keine Elemente werden ignoriert. Zum Beispiel: Übergabelambda x: x in " \t"
wenn Sie Zeilen als Zeichenketten vergleichen und sich nicht an Leerzeichen oder harten Tabs ausrichten möchten.
Die optionalen Argumente a und b sind die zu vergleichenden Sequenzen; beide sind standardmäßig leere Zeichenketten. Die Elemente beider Sequenzen müssen hashbar sein.
Das optionale Argument autojunk kann verwendet werden, um die automatische Junk-Heuristik zu deaktivieren.
Geändert in Version 3.2: Parameter autojunk hinzugefügt.
SequenceMatcher-Objekte erhalten drei Datenattribute: bjunk ist die Menge der Elemente von b, für die isjunk
Trueist; bpopular ist die Menge der Nicht-Junk-Elemente, die von der Heuristik als beliebt erachtet werden (wenn sie nicht deaktiviert ist); b2j ist ein Dictionary, das die verbleibenden Elemente von b auf eine Liste der Positionen abbildet, an denen sie vorkommen. Alle drei werden zurückgesetzt, wann immer b mitset_seqs()oderset_seq2()zurückgesetzt wird.Hinzugefügt in Version 3.2: Die Attribute bjunk und bpopular.
SequenceMatcher-Objekte haben die folgenden Methoden- set_seqs(a, b)¶
Setzt die beiden zu vergleichenden Sequenzen.
SequenceMatcherberechnet und speichert detaillierte Informationen über die zweite Sequenz. Wenn Sie also eine Sequenz mit vielen Sequenzen vergleichen möchten, verwenden Sieset_seq2(), um die häufig verwendete Sequenz einmal festzulegen, und rufen Sieset_seq1()wiederholt auf, einmal für jede der anderen Sequenzen.- set_seq1(a)¶
Setzt die erste zu vergleichende Sequenz. Die zweite zu vergleichende Sequenz wird nicht geändert.
- set_seq2(b)¶
Setzt die zweite zu vergleichende Sequenz. Die erste zu vergleichende Sequenz wird nicht geändert.
- find_longest_match(alo=0, ahi=None, blo=0, bhi=None)¶
Findet den längsten übereinstimmenden Block in
a[alo:ahi]undb[blo:bhi].Wenn isjunk weggelassen oder
Nonewar, gibtfind_longest_match()(i, j, k)zurück, so dassa[i:i+k]gleichb[j:j+k]ist, wobeialo <= i <= i+k <= ahiundblo <= j <= j+k <= bhi. Für alle(i', j', k'), die diese Bedingungen erfüllen, gelten auch die zusätzlichen Bedingungenk >= k',i <= i'und wenni == i',j <= j'. Anders ausgedrückt: Von allen maximalen übereinstimmenden Blöcken wird einer zurückgegeben, der in a am frühesten beginnt, und von allen maximalen übereinstimmenden Blöcken, die in a am frühesten beginnen, wird derjenige zurückgegeben, der in b am frühesten beginnt.>>> s = SequenceMatcher(None, " abcd", "abcd abcd") >>> s.find_longest_match(0, 5, 0, 9) Match(a=0, b=4, size=5)
Wenn isjunk bereitgestellt wurde, wird zunächst der längste übereinstimmende Block wie oben ermittelt, jedoch mit der zusätzlichen Einschränkung, dass kein Junk-Element im Block vorkommt. Dann wird dieser Block so weit wie möglich erweitert, indem (nur) Junk-Elemente auf beiden Seiten übereinstimmen. Der resultierende Block stimmt also nie mit Junk überein, außer wenn identischer Junk zufällig neben einer interessanten Übereinstimmung liegt.
Hier ist dasselbe Beispiel wie zuvor, aber unter Berücksichtigung von Leerzeichen als Junk. Dies verhindert, dass
' abcd'direkt mit dem' abcd'am Ende der zweiten Sequenz übereinstimmt. Stattdessen kann nur'abcd'übereinstimmen und mit dem am weitesten links stehenden'abcd'in der zweiten Sequenz übereinstimmen>>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd") >>> s.find_longest_match(0, 5, 0, 9) Match(a=1, b=0, size=4)
Wenn keine Blöcke übereinstimmen, gibt dies
(alo, blo, 0)zurück.Diese Methode gibt ein benanntes Tupel
Match(a, b, size)zurück.Geändert in Version 3.9: Standardargumente hinzugefügt.
- get_matching_blocks()¶
Gibt eine Liste von Tripeln zurück, die nicht überlappende übereinstimmende Teilsequenzen beschreiben. Jedes Tripel hat die Form
(i, j, n)und bedeutet, dassa[i:i+n] == b[j:j+n]. Die Tripel sind monoton steigend in i und j.Das letzte Tripel ist ein Dummy und hat den Wert
(len(a), len(b), 0). Es ist das einzige Tripel mitn == 0. Wenn(i, j, n)und(i', j', n')aufeinanderfolgende Tripel in der Liste sind und das zweite nicht das letzte Tripel in der Liste ist, dann gilti+n < i'oderj+n < j'; mit anderen Worten, aufeinanderfolgende Tripel beschreiben immer nicht aufeinanderfolgende gleiche Blöcke.>>> s = SequenceMatcher(None, "abxcd", "abcd") >>> s.get_matching_blocks() [Match(a=0, b=0, size=2), Match(a=3, b=2, size=2), Match(a=5, b=4, size=0)]
- get_opcodes()¶
Gibt eine Liste von 5-Tupeln zurück, die beschreiben, wie a in b umgewandelt wird. Jedes Tupel hat die Form
(tag, i1, i2, j1, j2). Das erste Tupel hati1 == j1 == 0, und die verbleibenden Tupel haben i1 gleich dem i2 des vorhergehenden Tupels und, ebenso, j1 gleich dem vorherigen j2.Die tag-Werte sind Zeichenketten mit diesen Bedeutungen:
Wert
Bedeutung
'replace'a[i1:i2]sollte durchb[j1:j2]ersetzt werden.'delete'a[i1:i2]sollte gelöscht werden. Beachten Sie, dass in diesem Fallj1 == j2gilt.'insert'b[j1:j2]sollte ana[i1:i1]eingefügt werden. Beachten Sie, dass in diesem Falli1 == i2gilt.'equal'a[i1:i2] == b[j1:j2](die Teilsequenzen sind gleich).Zum Beispiel
>>> a = "qabxcd" >>> b = "abycdf" >>> s = SequenceMatcher(None, a, b) >>> for tag, i1, i2, j1, j2 in s.get_opcodes(): ... print('{:7} a[{}:{}] --> b[{}:{}] {!r:>8} --> {!r}'.format( ... tag, i1, i2, j1, j2, a[i1:i2], b[j1:j2])) delete a[0:1] --> b[0:0] 'q' --> '' equal a[1:3] --> b[0:2] 'ab' --> 'ab' replace a[3:4] --> b[2:3] 'x' --> 'y' equal a[4:6] --> b[3:5] 'cd' --> 'cd' insert a[6:6] --> b[5:6] '' --> 'f'
- get_grouped_opcodes(n=3)¶
Gibt einen Generator von Gruppen mit bis zu n Zeilen Kontext zurück.
Ausgehend von den von
get_opcodes()zurückgegebenen Gruppen teilt diese Methode kleinere Änderungscluster auf und eliminiert dazwischen liegende Bereiche, die keine Änderungen aufweisen.Die Gruppen werden im gleichen Format wie von
get_opcodes()zurückgegeben.
- ratio()¶
Gibt ein Maß für die Ähnlichkeit der Sequenzen als Gleitkommazahl im Bereich [0, 1] zurück.
Wobei T die Gesamtzahl der Elemente in beiden Sequenzen ist und M die Anzahl der Übereinstimmungen ist, ist dies 2.0*M / T. Beachten Sie, dass dies
1.0ist, wenn die Sequenzen identisch sind, und0.0, wenn sie nichts gemeinsam haben.Die Berechnung ist teuer, wenn
get_matching_blocks()oderget_opcodes()noch nicht aufgerufen wurde. In diesem Fall sollten Sie vielleicht zuerstquick_ratio()oderreal_quick_ratio()versuchen, um eine Obergrenze zu erhalten.Hinweis
Vorsicht: Das Ergebnis eines Aufrufs von
ratio()kann von der Reihenfolge der Argumente abhängen. Zum Beispiel>>> SequenceMatcher(None, 'tide', 'diet').ratio() 0.25 >>> SequenceMatcher(None, 'diet', 'tide').ratio() 0.5
Die drei Methoden, die das Verhältnis von übereinstimmenden zu gesamten Zeichen zurückgeben, können aufgrund unterschiedlicher Approximationsgrade unterschiedliche Ergebnisse liefern, obwohl quick_ratio() und real_quick_ratio() immer mindestens so groß wie ratio() sind.
>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75
>>> s.quick_ratio()
0.75
>>> s.real_quick_ratio()
1.0
Beispiele für SequenceMatcher¶
Dieses Beispiel vergleicht zwei Zeichenketten und betrachtet Leerzeichen als "unerwünscht".
>>> s = SequenceMatcher(lambda x: x == " ",
... "private Thread currentThread;",
... "private volatile Thread currentThread;")
ratio() gibt eine Gleitkommazahl im Bereich [0, 1] zurück, die die Ähnlichkeit der Sequenzen misst. Als Faustregel gilt, dass ein ratio()-Wert über 0,6 bedeutet, dass die Sequenzen nahe beieinander liegen.
>>> print(round(s.ratio(), 3))
0.866
Wenn Sie nur daran interessiert sind, wo die Sequenzen übereinstimmen, ist get_matching_blocks() praktisch.
>>> for block in s.get_matching_blocks():
... print("a[%d] and b[%d] match for %d elements" % block)
a[0] and b[0] match for 8 elements
a[8] and b[17] match for 21 elements
a[29] and b[38] match for 0 elements
Beachten Sie, dass das letzte von get_matching_blocks() zurückgegebene Tupel immer ein Dummy ist, (len(a), len(b), 0), und dies ist der einzige Fall, in dem das letzte Tupel-Element (Anzahl der übereinstimmenden Elemente) 0 ist.
Wenn Sie wissen möchten, wie Sie die erste Sequenz in die zweite ändern können, verwenden Sie get_opcodes().
>>> for opcode in s.get_opcodes():
... print("%6s a[%d:%d] b[%d:%d]" % opcode)
equal a[0:8] b[0:8]
insert a[8:8] b[8:17]
equal a[8:29] b[17:38]
Siehe auch
Die Funktion
get_close_matches()in diesem Modul zeigt, wie einfacher Code, der aufSequenceMatcheraufbaut, für nützliche Arbeit verwendet werden kann.Rezept für einfache Versionskontrolle für eine kleine Anwendung, die mit
SequenceMatchererstellt wurde.
Differ-Objekte¶
Beachten Sie, dass von Differ generierte Deltas keinen Anspruch auf **minimale** Diffs erheben. Im Gegenteil, minimale Diffs sind oft kontraintuitiv, da sie überall synchronisieren, wo es möglich ist, manchmal zufällige Übereinstimmungen über 100 Seiten hinweg. Die Beschränkung von Synchronisationspunkten auf zusammenhängende Übereinstimmungen bewahrt eine gewisse Lokalität, auf Kosten der Produktion eines längeren Diffs.
Die Klasse Differ hat diesen Konstruktor
- class difflib.Differ(linejunk=None, charjunk=None)
Optionale Schlüsselwortparameter linejunk und charjunk sind für Filterfunktionen (oder
None).linejunk: Eine Funktion, die ein einzelnes Zeichenkettenargument akzeptiert und wahr zurückgibt, wenn die Zeichenkette unerwünscht ist. Der Standardwert ist
None, was bedeutet, dass keine Zeile als unerwünscht betrachtet wird.charjunk: Eine Funktion, die ein einzelnes Zeichen als Argument akzeptiert (eine Zeichenkette der Länge 1) und wahr zurückgibt, wenn das Zeichen unerwünscht ist. Der Standardwert ist
None, was bedeutet, dass kein Zeichen als unerwünscht betrachtet wird.Diese Junk-Filterfunktionen beschleunigen die Suche nach Unterschieden und führen nicht dazu, dass abweichende Zeilen oder Zeichen ignoriert werden. Lesen Sie die Beschreibung des isjunk-Parameters der Methode
find_longest_match()für eine Erklärung.Differ-Objekte werden (Deltas werden generiert) über eine einzige Methode verwendet:- compare(a, b)¶
Vergleicht zwei Sequenzen von Zeilen und generiert das Delta (eine Sequenz von Zeilen).
Jede Sequenz muss einzelne Zeilenstrings enthalten, die mit Zeilenumbrüchen enden. Solche Sequenzen können aus der `readlines()`-Methode von dateiähnlichen Objekten erhalten werden. Das generierte Delta besteht ebenfalls aus zeilenumbruchterminierten Strings, die bereit sind, als solche über die `writelines()`-Methode eines dateiähnlichen Objekts gedruckt zu werden.
Beispiel für Differ¶
Dieses Beispiel vergleicht zwei Texte. Zuerst richten wir die Texte ein, Sequenzen von einzelnen Zeilenstrings, die mit Zeilenumbrüchen enden (solche Sequenzen können auch aus der `readlines()`-Methode von dateiähnlichen Objekten erhalten werden).
>>> text1 = ''' 1. Beautiful is better than ugly.
... 2. Explicit is better than implicit.
... 3. Simple is better than complex.
... 4. Complex is better than complicated.
... '''.splitlines(keepends=True)
>>> len(text1)
4
>>> text1[0][-1]
'\n'
>>> text2 = ''' 1. Beautiful is better than ugly.
... 3. Simple is better than complex.
... 4. Complicated is better than complex.
... 5. Flat is better than nested.
... '''.splitlines(keepends=True)
Als Nächstes instanziieren wir ein Differ-Objekt.
>>> d = Differ()
Beachten Sie, dass wir beim Instanziieren eines Differ-Objekts Funktionen übergeben können, um Zeilen- und Zeichen-"Junk" herauszufiltern. Einzelheiten finden Sie im Konstruktor Differ().
Schließlich vergleichen wir die beiden.
>>> result = list(d.compare(text1, text2))
result ist eine Liste von Strings, also formatieren wir sie zur besseren Lesbarkeit.
>>> from pprint import pprint
>>> pprint(result)
[' 1. Beautiful is better than ugly.\n',
'- 2. Explicit is better than implicit.\n',
'- 3. Simple is better than complex.\n',
'+ 3. Simple is better than complex.\n',
'? ++\n',
'- 4. Complex is better than complicated.\n',
'? ^ ---- ^\n',
'+ 4. Complicated is better than complex.\n',
'? ++++ ^ ^\n',
'+ 5. Flat is better than nested.\n']
Als einzelner mehrzeiliger String sieht es so aus:
>>> import sys
>>> sys.stdout.writelines(result)
1. Beautiful is better than ugly.
- 2. Explicit is better than implicit.
- 3. Simple is better than complex.
+ 3. Simple is better than complex.
? ++
- 4. Complex is better than complicated.
? ^ ---- ^
+ 4. Complicated is better than complex.
? ++++ ^ ^
+ 5. Flat is better than nested.
Eine Befehlszeilenschnittstelle zu difflib¶
Dieses Beispiel zeigt, wie difflib verwendet werden kann, um ein Dienstprogramm im Stil von diff zu erstellen.
""" Command line interface to difflib.py providing diffs in four formats:
* ndiff: lists every line and highlights interline changes.
* context: highlights clusters of changes in a before/after format.
* unified: highlights clusters of changes in an inline format.
* html: generates side by side comparison with change highlights.
"""
import sys, os, difflib, argparse
from datetime import datetime, timezone
def file_mtime(path):
t = datetime.fromtimestamp(os.stat(path).st_mtime,
timezone.utc)
return t.astimezone().isoformat()
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-c', action='store_true', default=False,
help='Produce a context format diff (default)')
parser.add_argument('-u', action='store_true', default=False,
help='Produce a unified format diff')
parser.add_argument('-m', action='store_true', default=False,
help='Produce HTML side by side diff '
'(can use -c and -l in conjunction)')
parser.add_argument('-n', action='store_true', default=False,
help='Produce a ndiff format diff')
parser.add_argument('-l', '--lines', type=int, default=3,
help='Set number of context lines (default 3)')
parser.add_argument('fromfile')
parser.add_argument('tofile')
options = parser.parse_args()
n = options.lines
fromfile = options.fromfile
tofile = options.tofile
fromdate = file_mtime(fromfile)
todate = file_mtime(tofile)
with open(fromfile) as ff:
fromlines = ff.readlines()
with open(tofile) as tf:
tolines = tf.readlines()
if options.u:
diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n)
elif options.n:
diff = difflib.ndiff(fromlines, tolines)
elif options.m:
diff = difflib.HtmlDiff().make_file(fromlines,tolines,fromfile,tofile,context=options.c,numlines=n)
else:
diff = difflib.context_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n)
sys.stdout.writelines(diff)
if __name__ == '__main__':
main()
Beispiel für ndiff¶
Dieses Beispiel zeigt die Verwendung von difflib.ndiff().
"""ndiff [-q] file1 file2
or
ndiff (-r1 | -r2) < ndiff_output > file1_or_file2
Print a human-friendly file difference report to stdout. Both inter-
and intra-line differences are noted. In the second form, recreate file1
(-r1) or file2 (-r2) on stdout, from an ndiff report on stdin.
In the first form, if -q ("quiet") is not specified, the first two lines
of output are
-: file1
+: file2
Each remaining line begins with a two-letter code:
"- " line unique to file1
"+ " line unique to file2
" " line common to both files
"? " line not present in either input file
Lines beginning with "? " attempt to guide the eye to intraline
differences, and were not present in either input file. These lines can be
confusing if the source files contain tab characters.
The first file can be recovered by retaining only lines that begin with
" " or "- ", and deleting those 2-character prefixes; use ndiff with -r1.
The second file can be recovered similarly, but by retaining only " " and
"+ " lines; use ndiff with -r2; or, on Unix, the second file can be
recovered by piping the output through
sed -n '/^[+ ] /s/^..//p'
"""
__version__ = 1, 7, 0
import difflib, sys
def fail(msg):
out = sys.stderr.write
out(msg + "\n\n")
out(__doc__)
return 0
# open a file & return the file object; gripe and return 0 if it
# couldn't be opened
def fopen(fname):
try:
return open(fname)
except IOError as detail:
return fail("couldn't open " + fname + ": " + str(detail))
# open two files & spray the diff to stdout; return false iff a problem
def fcompare(f1name, f2name):
f1 = fopen(f1name)
f2 = fopen(f2name)
if not f1 or not f2:
return 0
a = f1.readlines(); f1.close()
b = f2.readlines(); f2.close()
for line in difflib.ndiff(a, b):
print(line, end=' ')
return 1
# crack args (sys.argv[1:] is normal) & compare;
# return false iff a problem
def main(args):
import getopt
try:
opts, args = getopt.getopt(args, "qr:")
except getopt.error as detail:
return fail(str(detail))
noisy = 1
qseen = rseen = 0
for opt, val in opts:
if opt == "-q":
qseen = 1
noisy = 0
elif opt == "-r":
rseen = 1
whichfile = val
if qseen and rseen:
return fail("can't specify both -q and -r")
if rseen:
if args:
return fail("no args allowed with -r option")
if whichfile in ("1", "2"):
restore(whichfile)
return 1
return fail("-r value must be 1 or 2")
if len(args) != 2:
return fail("need 2 filename args")
f1name, f2name = args
if noisy:
print('-:', f1name)
print('+:', f2name)
return fcompare(f1name, f2name)
# read ndiff output from stdin, and print file1 (which=='1') or
# file2 (which=='2') to stdout
def restore(which):
restored = difflib.restore(sys.stdin.readlines(), which)
sys.stdout.writelines(restored)
if __name__ == '__main__':
main(sys.argv[1:])