Python: diferencia entre dos cadenas

87

Me gustaría almacenar muchas palabras en una lista. Muchas de estas palabras son muy similares. Por ejemplo, tengo palabra afrykanerskojęzycznyy muchos de palabras como afrykanerskojęzycznym, afrykanerskojęzyczni, nieafrykanerskojęzyczni. ¿Cuál es la solución efectiva (rápida y con un tamaño de diferencia pequeño) para encontrar la diferencia entre dos cadenas y restaurar la segunda cadena de la primera y la diferencia?

usuario2626682
fuente
1
¿Qué quieres decir con "restaurar la segunda cadena a partir de la primera y diferenciar"?
jrd1
2
Creo que quiere decir "Haz que la segunda cuerda sea igual a la primera".
Elias Benevedes
1
@EliasBenevedes, exactamente :).
user2626682
1
¿Estás buscando algo como difflib? Si es así, consulte, por ejemplo, stackoverflow.com/questions/774316/…
torek

Respuestas:

109

Puede usar ndiff en el módulo difflib para hacer esto. Tiene toda la información necesaria para convertir una cadena en otra cadena.

Un simple ejemplo:

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

huellas dactilares:

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
perro
fuente
14
1 Python tiene tan muchos módulos útiles. Parece que aprendo uno nuevo cada día.
arshajii
1
Esto es superar la diferencia manualmente; restaurar la diferencia entre las dos cadenas, por supuesto, es mucho más fácil con difflib.restore
dawg
¡Gracias! Pero no estoy seguro de si esto es eficiente en 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
ndiffes un generador por lo que es bastante eficiente en memoria. Lo está invocando, listlo que convierte las comparaciones de caracteres generadas individualmente en una lista completa de ellos. Solo tendría unos pocos en la memoria a la vez si no lo llamara list.
dawg
1
También funciona en Python 2 (para mí) Sugeriría hacer una pregunta con la fuente específica y la salida específica. No puedo depurar en los comentarios ...
dawg
24

Me gusta la respuesta ndiff, pero si desea escupirlo todo en una lista de solo los cambios, puede hacer algo como:

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
fuente
3
Esto es justo lo que buscaba en Google. Una nota rápida, @Eric, sus variables no coinciden como se muestra hoy, 20180905. O 1) cambie la última línea a output_list = [li for li in list(difflib.ndiff(case_a,case_b)) if li[0] != ' ']o 2) Cambie los nombres de las variables de cadena como case_a -> ay case_b -> b. ¡Salud!
bballdave025
3
También puede resultar útil mostrar el resultado de su comando >>> output_list:; # resultado #['- b', '+ a', '+ m']
bballdave025
2
if not li.startswith(' ')es el equivalente de if li[0] != ' 'Algunos pueden encontrarlo más legible. O inclusoif item.startswith(('-', '+', ))
dmmfll
@DMfll Downvote. Las listas no tienen a startswith()partir de Python3.7.4
Nathan
3

Puede mirar en el módulo regex (la sección difusa). No sé si puede obtener las diferencias reales, pero al menos puede especificar el número permitido de diferentes tipos de cambios como insertar, eliminar y sustituciones:

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
fuente
3

Lo que está pidiendo es una forma especializada de compresión. xdelta3 fue diseñado para este tipo particular de compresión, y hay un enlace de Python para él, pero probablemente podría salirse con la suya usando zlib directamente. Le gustaría usar zlib.compressobjy zlib.decompressobjcon el zdictparámetro establecido en su "palabra base", por ejemplo afrykanerskojęzyczny.

Las advertencias zdictsolo se admiten en Python 3.3 y versiones posteriores, y es más fácil de codificar si tiene la misma "palabra base" para todas sus diferencias, que puede ser o no lo que desea.

Craig Silverstein
fuente
-2

La respuesta a mi comentario anterior sobre la pregunta original me hace pensar que esto es todo lo que quiere:

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

Esto hará lo siguiente:

Para cada valor de la lista de palabras, establezca ese valor de la lista de palabras en el código original.

Todo lo que tiene que hacer es colocar este fragmento de código donde necesita cambiar la lista de palabras, asegurándose de almacenar las palabras que necesita cambiar en la lista de palabras y que la palabra original es correcta.

¡Espero que esto ayude!

Elías Benevedes
fuente
Gracias, pero en realidad me gustaría almacenar palabras como 'nieafrykanerskojęzyczni' de una manera eficiente para la memoria, usando similitudes con 'afrykanerskojęzyczny'.
user2626682