Python - разница между двумя строками

87

Я хотел бы сохранить в списке много слов. Многие из этих слов очень похожи. Например , у меня есть слово , afrykanerskojęzycznyи многие из таких слов , как afrykanerskojęzycznym, afrykanerskojęzyczni, nieafrykanerskojęzyczni. Каково эффективное (быстрое и дающее небольшой размер разницы) решение для поиска разницы между двумя строками и восстановления второй строки из первой и разницы?

user2626682
источник
1
Что вы имеете в виду под «восстановить вторую строку из первой и сравнить»?
jrd1
2
Я считаю, что он имеет в виду: «Сделайте вторую струну такой же, как первую».
Элиас Беневедес
1
@EliasBenevedes, именно так :).
user2626682
1
Вы ищете что-то подобное difflib? Если да, см., Например, stackoverflow.com/questions/774316/…
torek

Ответы:

109

Для этого вы можете использовать ndiff в модуле diffflib. В нем есть вся информация, необходимая для преобразования одной строки в другую.

Простой пример:

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

печатает:

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 так много полезных модулей. Кажется, я узнаю о новом каждый день.
arshajii
1
Это ручное преодоление различий; восстановить различия между двумя строками, конечно, намного проще с difflib.restore
dawg
Благодарность! Но я не уверен, эффективно ли это с точки зрения памяти. list (diffflib.ndiff ("африканерской жензычны", "неафриканерской жензычны")) ['+ n', '+ i', '+ e', 'a', 'f', 'r', 'y', 'k' , 'a', 'n', 'e', ​​'r', 's', 'k', 'o', 'j', 'ę', 'z', 'y', 'c', ' z ',' n ',' y ']
user2626682
ndiffявляется генератором, поэтому он довольно эффективен с точки зрения памяти. Вы вызываете listего, который превращает индивидуально сгенерированные сравнения персонажей в их полный список. Если бы вы не обращались listк нему, у вас было бы всего несколько в памяти одновременно .
dawg
1
Также работает на Python 2 (для меня) Я бы предложил задать вопрос с конкретным источником и конкретным выводом. Я не могу отлаживать в комментариях ...
dawg 06
24

Мне нравится ответ ndiff, но если вы хотите выложить все это в список только изменений, вы можете сделать что-то вроде:

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
Это как раз то, для чего я искал в Google. Одно небольшое примечание, @Eric, ваши переменные не совпадают, как показано сегодня, 20180905. Либо 1) измените последнюю строку на, output_list = [li for li in list(difflib.ndiff(case_a,case_b)) if li[0] != ' ']либо 2) измените имена строковых переменных на case_a -> aи case_b -> b. Ура!
bballdave025 05
3
Также может быть полезно показать вывод вашей команды >>> output_list:; # результат #['- b', '+ a', '+ m']
bballdave025 05
2
if not li.startswith(' ')является эквивалентом if li[0] != ' 'Некоторые могут найти его более разборчивым. Или дажеif item.startswith(('-', '+', ))
dmmfll
@DMfll Downvote. В списках нет startswith()питона3.7.4
Натан
3

Вы можете заглянуть в модуль регулярных выражений (нечеткий раздел). Я не знаю, сможете ли вы получить реальные различия, но, по крайней мере, вы можете указать допустимое количество различных типов изменений, таких как вставка, удаление и замены:

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
источник
3

Вы просите об особой форме сжатия. xdelta3 был разработан для этого конкретного типа сжатия, и для него есть привязка к python, но вы, вероятно, можете обойтись без использования zlib напрямую. Вы хотели бы использовать zlib.compressobjи zlib.decompressobjс zdictпараметром, установленным на ваше «базовое слово», например afrykanerskojęzyczny.

Предостережения zdictподдерживаются только в Python 3.3 и выше, и проще всего кодировать, если у вас есть одно и то же «базовое слово» для всех ваших различий, которое может быть или не быть тем, что вы хотите.

Крейг Сильверстайн
источник
-2

Ответ на мой комментарий выше по исходному вопросу заставляет меня думать, что это все, что он хочет:

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

Это сделает следующее:

Для каждого значения в списке слов установите для этого значения списка слов исходный код.

Все, что вам нужно сделать, это поместить этот фрагмент кода туда, где вам нужно изменить список слов, убедившись, что вы сохранили слова, которые нужно изменить, в списке слов, и что исходное слово правильное.

Надеюсь это поможет!

Элиас Беневедес
источник
Спасибо, но на самом деле я хотел бы хранить такие слова, как «nieafrykanerskojęzyczni», с эффективным использованием памяти, используя сходство с «afrykanerskojęzyczny».
user2626682