Python - differenza tra due stringhe


90

Vorrei memorizzare molte parole in un elenco. Molte di queste parole sono molto simili. Per esempio io ho la parola afrykanerskojęzycznye molte delle parole come afrykanerskojęzycznym, afrykanerskojęzyczni, nieafrykanerskojęzyczni. Qual è la soluzione efficace (veloce e di piccole dimensioni diff) per trovare la differenza tra due stringhe e ripristinare la seconda stringa dalla prima e diff?


1
Cosa intendi con "ripristina la seconda stringa dalla prima e diff"?
jrd1

2
Credo che significhi "Rendi la seconda stringa uguale alla prima".
Elias Benevedes

1
@EliasBenevedes, esattamente :).
user2626682

1
Cerchi qualcosa di simile difflib? Se è così, vedi, ad esempio, stackoverflow.com/questions/774316/...
Torek

Risposte:


113

Puoi usare ndiff nel modulo difflib per farlo. Ha tutte le informazioni necessarie per convertire una stringa in un'altra stringa.

Un semplice esempio:

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

stampe:

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

14
+1 Python ha così tanti moduli utili. Sembra che ne apprenda uno nuovo ogni giorno.
arshajii

1
Questo è superare manualmente la differenza; ripristinare la differenza tra le due corde, ovviamente, è molto più semplice con difflib.restore
dawg

Grazie! Ma non sono sicuro che sia efficiente in termini di memoria. 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

ndiffè un generatore quindi è abbastanza efficiente in termini di memoria. Lo stai chiamando, listil che trasforma i confronti dei personaggi generati individualmente in un elenco completo di essi. Ne avresti solo pochi in memoria alla volta se non lo avessi richiamato list.
dawg

1
Funziona anche su Python 2 (per me) suggerirei di fare una domanda con la fonte specifica e l'output specifico. Non riesco a eseguire il debug nei commenti ...
dawg

26

Mi piace la risposta ndiff, ma se vuoi sputare tutto in un elenco delle sole modifiche, potresti fare qualcosa del tipo:

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] != ' ']

3
Questo è proprio quello per cui stavo cercando su Google. Una breve nota, @Eric, le tue variabili non corrispondono come mostrato oggi, 20180905. O 1) cambia l'ultima riga in output_list = [li for li in list(difflib.ndiff(case_a,case_b)) if li[0] != ' ']o 2) Cambia i nomi delle variabili stringa come case_a -> ae case_b -> b. Saluti!
bballdave025

4
Potrebbe anche essere utile mostrare l'output del comando >>> output_list:; # risultato #['- b', '+ a', '+ m']
bballdave025

2
if not li.startswith(' ')è l'equivalente di if li[0] != ' 'Alcuni potrebbero trovarlo più leggibile. O ancheif item.startswith(('-', '+', ))
dmmfll

@ DMfll Downvote. Gli elenchi non hanno startswith()come di python3.7.4
Nathan

3

Puoi guardare nel modulo regex (la sezione fuzzy). Non so se puoi ottenere le differenze effettive, ma almeno puoi specificare il numero consentito di diversi tipi di modifiche come inserimento, eliminazione e sostituzioni:

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'

3

Quello che stai chiedendo è una forma specializzata di compressione. xdelta3 è stato progettato per questo particolare tipo di compressione, e c'è un binding python per questo, ma probabilmente potresti cavartela usando direttamente zlib. Vorresti usare zlib.compressobje zlib.decompressobjcon il zdictparametro impostato sulla tua "parola base", ad es afrykanerskojęzyczny.

Le avvertenze sono zdictsupportate solo in python 3.3 e versioni successive, ed è più facile codificare se hai la stessa "parola di base" per tutti i tuoi diff, che può essere o meno quello che vuoi.


-2

La risposta al mio commento sopra sulla domanda originale mi fa pensare che questo sia tutto ciò che vuole:

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

Questo farà quanto segue:

Per ogni valore nell'elenco di parole, imposta quel valore dell'elenco di parole sul codice originale.

Tutto quello che devi fare è inserire questo pezzo di codice dove devi cambiare l'elenco di parole, assicurandoti di memorizzare le parole che devi cambiare nell'elenco di parole e che la parola originale sia corretta.

Spero che sia di aiuto!


Grazie, ma in realtà mi piacerebbe memorizzare parole come "nieafrykanerskojęzyczni" in modo efficiente in termini di memoria, usando la somiglianza con "afrykanerskojęzyczny".
user2626682
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.