Python - Unterschied zwischen zwei Zeichenfolgen

88

Ich möchte viele Wörter in einer Liste speichern. Viele dieser Wörter sind sehr ähnlich. Zum Beispiel habe ich Wort afrykanerskojęzycznyund viele Wörter wie afrykanerskojęzycznym, afrykanerskojęzyczni, nieafrykanerskojęzyczni. Was ist die effektive (schnelle und kleine Diff-Größe) Lösung, um den Unterschied zwischen zwei Strings zu finden und den zweiten String von dem ersten und dem Diff wiederherzustellen?

user2626682
quelle
1
Was meinst du mit "Wiederherstellen der zweiten Zeichenfolge aus der ersten und diff"?
jrd1
2
Ich glaube, er meint "Mach die zweite Saite genauso wie die erste".
Elias Benevedes
1
@ EliasBenevedes, genau :).
user2626682
1
Suchen Sie so etwas wie difflib? Wenn ja, siehe z. B. stackoverflow.com/questions/774316/…
torek

Antworten:

112

Sie können dazu ndiff im difflib-Modul verwenden. Es enthält alle Informationen, die zum Konvertieren einer Zeichenfolge in eine andere Zeichenfolge erforderlich sind.

Ein einfaches Beispiel:

import difflib

cases=[('afrykanerskojęzyczny', 'afrykanerskojęzycznym'),
       ('afrykanerskojęzyczni', 'nieafrykanerskojęzyczni'),
       ('afrykanerskojęzycznym', 'afrykanerskojęzyczny'),
       ('nieafrykanerskojęzyczni', 'afrykanerskojęzyczni'),
       ('nieafrynerskojęzyczni', 'afrykanerskojzyczni'),
       ('abcdefg','xac')] 

for a,b in cases:     
    print('{} => {}'.format(a,b))  
    for i,s in enumerate(difflib.ndiff(a, b)):
        if s[0]==' ': continue
        elif s[0]=='-':
            print(u'Delete "{}" from position {}'.format(s[-1],i))
        elif s[0]=='+':
            print(u'Add "{}" to position {}'.format(s[-1],i))    
    print()      

Drucke:

afrykanerskojęzyczny => afrykanerskojęzycznym
Add "m" to position 20

afrykanerskojęzyczni => nieafrykanerskojęzyczni
Add "n" to position 0
Add "i" to position 1
Add "e" to position 2

afrykanerskojęzycznym => afrykanerskojęzyczny
Delete "m" from position 20

nieafrykanerskojęzyczni => afrykanerskojęzyczni
Delete "n" from position 0
Delete "i" from position 1
Delete "e" from position 2

nieafrynerskojęzyczni => afrykanerskojzyczni
Delete "n" from position 0
Delete "i" from position 1
Delete "e" from position 2
Add "k" to position 7
Add "a" to position 8
Delete "ę" from position 16

abcdefg => xac
Add "x" to position 0
Delete "b" from position 2
Delete "d" from position 4
Delete "e" from position 5
Delete "f" from position 6
Delete "g" from position 7
dawg
quelle
14
+1 Python hat so viele nützliche Module. Es scheint, dass ich jeden Tag etwas Neues lerne.
Arshajii
1
Dies geht manuell durch den Unterschied; Das Wiederherstellen des Unterschieds zwischen den beiden Saiten ist natürlich mit difflib.restore
dawg
Vielen Dank! Aber ich bin mir nicht sicher, ob dies speichereffizient ist. list (difflib.ndiff ("afrykanerskojęzyczny", "nieafrykanerskojęzyczny")) ['+ n', '+ i', '+ e', 'a', 'f', 'r', 'y', 'k' , 'a', 'n', 'e', ​​'r', 's', 'k', 'o', 'j', 'ę', 'z', 'y', 'c', ' z ',' n ',' y ']
user2626682
ndiffist ein Generator und daher sehr speichereffizient. Sie rufen es listauf, wodurch die individuell generierten Zeichenvergleiche in eine vollständige Liste umgewandelt werden. Sie würden immer nur wenige gleichzeitig im Speicher haben, wenn Sie nicht darauf zugreifen würden list.
Morgengrauen
1
Funktioniert auch mit Python 2 (für mich) Ich würde vorschlagen, eine Frage mit der spezifischen Quelle und der spezifischen Ausgabe zu stellen. Ich kann nicht in Kommentaren debuggen ...
dawg
25

Ich mag die Antwort von ndiff, aber wenn Sie alles in eine Liste nur der Änderungen spucken möchten, können Sie Folgendes tun:

import difflib

case_a = 'afrykbnerskojęzyczny'
case_b = 'afrykanerskojęzycznym'

output_list = [li for li in difflib.ndiff(case_a, case_b) if li[0] != ' ']
Eric
quelle
3
Genau dafür habe ich gegoogelt. Ein kurzer Hinweis, @Eric, Ihre Variablen stimmen nicht wie heute gezeigt überein, 20180905. Entweder 1) ändern Sie die letzte Zeile in output_list = [li for li in list(difflib.ndiff(case_a,case_b)) if li[0] != ' ']oder 2) Ändern Sie die Namen der Zeichenfolgenvariablen als case_a -> aund case_b -> b. Prost!
bballdave025
4
Es kann auch hilfreich sein, die Ausgabe Ihres Befehls anzuzeigen : >>> output_list; # Ergebnis #['- b', '+ a', '+ m']
bballdave025
2
if not li.startswith(' ')ist das Äquivalent von if li[0] != ' 'Einige mögen es lesbarer finden. Oder sogarif item.startswith(('-', '+', ))
dmmfll
@ DMfll Downvote. Listen haben nicht startswith()ab Python3.7.4
Nathan
3

Sie können in das Regex-Modul (den Fuzzy-Bereich) schauen . Ich weiß nicht, ob Sie die tatsächlichen Unterschiede erhalten können, aber zumindest können Sie die zulässige Anzahl verschiedener Arten von Änderungen wie Einfügen, Löschen und Ersetzen angeben:

import regex
sequence = 'afrykanerskojezyczny'
queries = [ 'afrykanerskojezycznym', 'afrykanerskojezyczni', 
            'nieafrykanerskojezyczni' ]
for q in queries:
    m = regex.search(r'(%s){e<=2}'%q, sequence)
    print 'match' if m else 'nomatch'
perreal
quelle
3

Was Sie verlangen, ist eine spezielle Form der Komprimierung. xdelta3 wurde für diese spezielle Art der Komprimierung entwickelt, und es gibt eine Python-Bindung dafür, aber Sie könnten wahrscheinlich mit der direkten Verwendung von zlib davonkommen. Sie möchten zlib.compressobjund zlib.decompressobjmit dem zdictauf Ihr "Basiswort" eingestellten Parameter verwenden, z afrykanerskojęzyczny.

Vorsichtsmaßnahmen werden zdictnur in Python 3.3 und höher unterstützt, und es ist am einfachsten zu codieren, wenn Sie für alle Ihre Unterschiede das gleiche "Basiswort" haben, das möglicherweise das ist, was Sie wollen oder nicht.

Craig Silverstein
quelle
-2

Die Antwort auf meinen obigen Kommentar zur Originalfrage lässt mich denken, dass dies alles ist, was er will:

loopnum = 0
word = 'afrykanerskojęzyczny'
wordlist = ['afrykanerskojęzycznym','afrykanerskojęzyczni','nieafrykanerskojęzyczni']
for i in wordlist:
    wordlist[loopnum] = word
    loopnum += 1

Dies führt Folgendes aus:

Setzen Sie für jeden Wert in der Wortliste diesen Wert der Wortliste auf den ursprünglichen Code.

Alles, was Sie tun müssen, ist, diesen Code dort abzulegen, wo Sie die Wortliste ändern müssen. Stellen Sie dabei sicher, dass Sie die Wörter, die Sie ändern müssen, in der Wortliste speichern und dass das ursprüngliche Wort korrekt ist.

Hoffe das hilft!

Elias Benevedes
quelle
Danke, aber eigentlich möchte ich Wörter wie 'nieafrykanerskojęzyczni' speichereffizient speichern und dabei die Ähnlichkeit mit 'afrykanerskojęzyczny' verwenden.
user2626682