Python - różnica między dwoma ciągami znaków

87

Chciałbym przechowywać dużo słów na liście. Wiele z tych słów jest bardzo podobnych. Na przykład mam słowa afrykanerskojęzycznyi wiele słów, jak afrykanerskojęzycznym, afrykanerskojęzyczni, nieafrykanerskojęzyczni. Jakie jest skuteczne (szybkie i dające mały rozmiar różnic) rozwiązanie, aby znaleźć różnicę między dwoma łańcuchami i przywrócić drugi ciąg z pierwszego i diff?

user2626682
źródło
1
Co masz na myśli mówiąc „przywróć drugi ciąg z pierwszego i porównaj”?
jrd1
2
Myślę, że ma na myśli „Niech druga struna będzie taka sama jak pierwsza”.
Elias Benevedes
1
@EliasBenevedes, dokładnie :).
user2626682
1
Szukasz czegoś takiego difflib? Jeśli tak, zobacz, np stackoverflow.com/questions/774316/...
torek

Odpowiedzi:

109

Aby to zrobić, możesz użyć ndiff w module difflib. Zawiera wszystkie informacje niezbędne do konwersji jednego ciągu na inny.

Prosty przykład:

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

wydruki:

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
psie
źródło
14
+1 Python ma tak wiele przydatnych modułów. Wydaje się, że każdego dnia dowiaduję się o nowym.
arshajii
1
To jest ręczne przejście przez różnicę; przywrócenie różne między dwoma strunami, oczywiście, jest o wiele łatwiej z difflib.restore
Dawg
Dzięki! Ale nie jestem pewien, czy to wydajna pamięć. 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
ndiffjest generatorem, więc jest dość wydajny w pamięci. Wołasz listgo, który zamienia indywidualnie wygenerowane porównania postaci w pełną ich listę. Gdybyś go nie wzywał, miałbyś w pamięci tylko kilka na raz list.
dawg
1
Działa również na Pythonie 2 (dla mnie) Proponuję zadać pytanie z konkretnym źródłem i konkretnym wyjściem. Nie mogę debugować w komentarzach ...
dawg
24

Podoba mi się odpowiedź ndiff, ale jeśli chcesz wypluć to wszystko na listę samych zmian, możesz zrobić coś takiego:

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
źródło
3
Właśnie po to szukałem w Google. Jedna krótka uwaga, @Eric, twoje zmienne nie pasują do siebie, jak pokazano dzisiaj, 20180905. Albo 1) zmień ostatnią linię na output_list = [li for li in list(difflib.ndiff(case_a,case_b)) if li[0] != ' ']lub 2) Zmień nazwy zmiennych łańcuchowych na case_a -> ai case_b -> b. Twoje zdrowie!
bballdave025
3
Pomocne może być również wyświetlenie wyniku działania polecenia >>> output_list:; # wynik #['- b', '+ a', '+ m']
bballdave025
2
if not li.startswith(' ')jest odpowiednikiem dla if li[0] != ' 'Niektórzy mogą uznać to za bardziej czytelne. Lub nawetif item.startswith(('-', '+', ))
dmmfll
@DMfll Downvote. Listy nie mają startswith()stanu od Pythona3.7.4
Nathan
3

Możesz zajrzeć do modułu regex (sekcja rozmyta). Nie wiem, czy możesz uzyskać rzeczywiste różnice, ale przynajmniej możesz określić dozwoloną liczbę różnych typów zmian, takich jak wstawianie, usuwanie i zastępowanie:

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
źródło
3

To, o co prosisz, to wyspecjalizowana forma kompresji. xdelta3 została zaprojektowana dla tego konkretnego rodzaju kompresji i jest do niej przypisana python, ale prawdopodobnie możesz uciec bezpośrednio używając zlib. Którą chcesz użyć zlib.compressobji zlib.decompressobjz zdictzestawu parametrów do „słowa bazowego”, np afrykanerskojęzyczny.

Ostrzeżenia są zdictobsługiwane tylko w Pythonie 3.3 i nowszych, a najłatwiej jest kodować, jeśli masz to samo „słowo bazowe” dla wszystkich różnic, co może być tym, czego chcesz lub nie.

Craig Silverstein
źródło
-2

Odpowiedź na mój komentarz powyżej do Pierwotnego pytania sprawia, że ​​myślę, że tylko tego chce:

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

Spowoduje to wykonanie następujących czynności:

Dla każdej wartości w liście słów, ustaw tę wartość listy słów na kod źródłowy.

Wszystko, co musisz zrobić, to umieścić ten fragment kodu w miejscu, w którym chcesz zmienić listę słów, upewniając się, że przechowujesz słowa, które chcesz zmienić w liście słów, i że oryginalne słowo jest poprawne.

Mam nadzieję że to pomoże!

Elias Benevedes
źródło
Dzięki, ale tak naprawdę chciałbym przechowywać słowa typu „nieafrykanerskojęzyczne” w sposób efektywny w pamięci, używając podobieństwa do „afrykanerskojęzyczny”.
user2626682