2016-08-17 5 views
1

Я использую difflib «s SequenceMatcher к get_opcodes() и чем выделить изменения с css, чтобы создать какое-то веб-diff.соответствий, слова, а не символы

Во-первых, я поставил min_delta, так что я считаю две строки различны, если только 3 или более символов всей строки различаются, один за другим (delta означает реальное, встречается дельту, которая суммирует все изменения один-символов) :

matcher = SequenceMatcher(source_str, diff_str) 
min_delta = 3 
delta = 0 

for tag, i1, i2, j1, j2 in matcher.get_opcodes(): 
    if tag == "equal": 
     continue # nothing to capture here 
    elif tag == "delete": 
     if source_str[i1:i2].isspace(): 
      continue # be whitespace-agnostic 
     else: 
      delta += (i2 - i1) # delete i2-i1 chars 
    elif tag == "replace": 
     if source_str[i1:i2].isspace() or diff_str[j1:j2].isspace(): 
      continue # be whitespace-agnostic 
     else: 
      delta += (i2 - i1) # replace i2-i1 chars 
    elif tag == "insert": 
     if diff_str[j1:j2].isspace(): 
      continue # be whitespace-agnostic 
     else: 
      delta += (j2 - j1) # insert j2-j1 chars 

return_value = True if (delta > min_delta) else False 

Это помогает мне определить, действительно ли две строки отличаются друг от друга. Не очень эффективно, но я не думал, что лучше.

Затем я раскрасить различия между двумя строками таким же образом:

for tag, i1, i2, j1, j2 in matcher.get_opcodes(): 
    if tag == "equal": 
     # bustling with strings, inserting them in <span>s and colorizing 
    elif tag == "delete": 
     # ... 

return_value = old_string, new_string 

И результат выглядит довольно уродливо (синий для замены зеленый новый и красный для удаления, ничего для равных):

example

Так, это происходит потому, что SequenceMatcher спичек каждый символ. Но я хочу, чтобы оно соответствовало каждому слову вместо этого (и, вероятно, пробелам вокруг них), или что-то еще более привлекательное, потому что, как вы можете видеть на скриншоте, первая книга действительно перемещена на четвертую позицию.

Мне кажется, что что-то может быть сделано с isjunk и autojunk параметров SequenceMatcher, но я не могу понять, как писать lambda с для моих целей.

Таким образом, у меня есть два вопросы:

  1. Можно ли соответствовать словами? Можно ли использовать get_opcodes() и SequenceMatcher? Если нет, что можно использовать вместо этого?

  2. Хорошо, это скорее следствие, но все же: если соответствие слов можно, то я могу избавиться от грязных писак с min_delta и вернуться True как только по крайней мере одно отличающимся словом, верно?

+1

... не возникло для вас, что 'True if delta> min_delta else False' * точно так же *, как просто' delta> min_delta'? – Bakuriu

+0

Хорошая точка! Когда я пишу код, это просто способ добавить больше ясности для себя. Я обычно реорганизую код после удаления ненужной многословности и встраивания некоторых операций. На этот раз я забыл сделать это на примере. – light2yellow

ответ

2

SequenceMatcher может принимать списки str в качестве входных данных.

Вы можете сначала разделить ввод на слова, а затем использовать SequenceMatcher, чтобы помочь вам отличить слова. Тогда ваш цветной diff будет словами вместо персонажами.

>>> def my_get_opcodes(a, b): 
...  s = SequenceMatcher(None, a, b) 
...  for tag, i1, i2, j1, j2 in s.get_opcodes(): 
...   print('{:7} a[{}:{}] --> b[{}:{}] {!r:>8} --> {!r}'.format(
...    tag, i1, i2, j1, j2, a[i1:i2], b[j1:j2])) 
... 

>>> my_get_opcodes("qabxcd", "abycdf") 
delete a[0:1] --> b[0:0]  'q' --> '' 
equal  a[1:3] --> b[0:2]  'ab' --> 'ab' 
replace a[3:4] --> b[2:3]  'x' --> 'y' 
equal  a[4:6] --> b[3:5]  'cd' --> 'cd' 
insert a[6:6] --> b[5:6]  '' --> 'f' 

# This is the bad result you currently have. 
>>> my_get_opcodes("one two three\n", "ore tree emu\n") 
equal  a[0:1] --> b[0:1]  'o' --> 'o' 
replace a[1:2] --> b[1:2]  'n' --> 'r' 
equal  a[2:5] --> b[2:5] 'e t' --> 'e t' 
delete a[5:10] --> b[5:5] 'wo th' --> '' 
equal  a[10:13] --> b[5:8] 'ree' --> 'ree' 
insert a[13:13] --> b[8:12]  '' --> ' emu' 
equal  a[13:14] --> b[12:13]  '\n' --> '\n' 

>>> my_get_opcodes("one two three\n".split(), "ore tree emu\n".split()) 
replace a[0:3] --> b[0:3] ['one', 'two', 'three'] --> ['ore', 'tree', 'emu'] 

# This may be the result you want. 
>>> my_get_opcodes("one two emily three ha\n".split(), "ore tree emily emu haha\n".split()) 
replace a[0:2] --> b[0:2] ['one', 'two'] --> ['ore', 'tree'] 
equal  a[2:3] --> b[2:3] ['emily'] --> ['emily'] 
replace a[3:5] --> b[3:5] ['three', 'ha'] --> ['emu', 'haha'] 

# A more complicated example exhibiting all four kinds of opcodes. 
>>> my_get_opcodes("one two emily three yo right end\n".split(), "ore tree emily emu haha yo yes right\n".split()) 
replace a[0:2] --> b[0:2] ['one', 'two'] --> ['ore', 'tree'] 
equal  a[2:3] --> b[2:3] ['emily'] --> ['emily'] 
replace a[3:4] --> b[3:5] ['three'] --> ['emu', 'haha'] 
equal  a[4:5] --> b[5:6] ['yo'] --> ['yo'] 
insert a[5:5] --> b[6:7]  [] --> ['yes'] 
equal  a[5:6] --> b[7:8] ['right'] --> ['right'] 
delete a[6:7] --> b[8:8] ['end'] --> [] 

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

Например:

  • Чтобы получить различия между по линии - Вы, вероятно, могли бы использовать splitlines()
  • Для дифф книгой - Вы, вероятно, могли бы реализовать функцию, которая срывает с 1., 2.
  • Для сравнения по сегментам - Вы можете использовать API таким образом ([book_1, author_1, year_1, book_2, author_2, ...], [book_1, author_1, year_1, book_2, author_2, ...]). И тогда ваша окраска будет по сегменту.
+0

Пожалуйста, не злоупотребляйте форматированием встроенного кода, чтобы выделить вещи, которые не являются ни кодом (например, именами классов/модулей/функций, малыми образцами кода и т. Д.), Или быть взятыми буквально в письменном виде (например, уникальный идентификатор, который должен быть написан точно так же отображается). Чтобы подчеркнуть другой контент, используйте либо * italics * (\ * italics \ *), либо ** bold ** (\ * \ * bold \ * \ *). – Bakuriu