Создание и применение diff в python
Есть ли способ "из коробки" в python создать список различий между двумя текстами, а затем применить этот diff к одному файлу для получения другого, позже?
Я хочу сохранить историю изменений в тексте, но я не хочу сохранять весь текст для каждой ревизии, если есть только одна отредактированная строка. Я смотрел на difflib, но я не мог понять, как создать список только отредактированных строк, которые все еще могут быть использованы для изменения одного текст для получения другого.
Ответы
Ответ 1
У вас есть взгляд на diff-match-patch из google? Очевидно, Google Docs использует этот набор алгоритмов. Он включает в себя не только модуль diff, но и модуль патча, поэтому вы можете сгенерировать новейший файл из более старых файлов и разностей.
Включена версия python.
http://code.google.com/p/google-diff-match-patch/
Ответ 2
Требуется ли difflib.unified_diff, которого вы хотите? Ниже приведен пример .
Ответ 3
Алгоритмы AFAIK most diff используют простое Longest Common Subsequence, чтобы найти общую часть между двумя текстами и все, что осталось, считается разница. Не нужно слишком сложно кодировать собственный алгоритм динамического программирования, чтобы выполнить это в python, страница с википедии выше также предоставляет алгоритм.
Ответ 4
Должно ли быть решение python?
Мое первое соображение относительно решения было бы использовать либо систему управления версиями (Subversion, Git, и т.д.), Либо утилиты diff
/patch
, которые являются стандартными для системы unix или являются частью cygwin
для системы на базе Windows.
Ответ 5
Я реализовал чистую функцию python для применения патчей diff для восстановления любой из входных строк, я надеюсь, что кто-то сочтет это полезным. Он использует разбор Unified diff format.
import re
_hdr_pat = re.compile("^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@$")
def apply_patch(s,patch,revert=False):
"""
Apply unified diff patch to string s to recover newer string.
If revert is True, treat s as the newer string, recover older string.
"""
s = s.splitlines(True)
p = patch.splitlines(True)
t = ''
i = sl = 0
(midx,sign) = (1,'+') if not revert else (3,'-')
while i < len(p) and p[i].startswith(("---","+++")): i += 1 # skip header lines
while i < len(p):
m = _hdr_pat.match(p[i])
if not m: raise Exception("Cannot process diff")
i += 1
l = int(m.group(midx))-1 + (m.group(midx+1) == '0')
t += ''.join(s[sl:l])
sl = l
while i < len(p) and p[i][0] != '@':
if i+1 < len(p) and p[i+1][0] == '\\': line = p[i][:-1]; i += 2
else: line = p[i]; i += 1
if len(line) > 0:
if line[0] == sign or line[0] == ' ': t += line[1:]
sl += (line[0] != sign)
t += ''.join(s[sl:])
return t
Если есть строки заголовка ("--- ...\n","+++ ...\n")
, он пропускает их. Если у нас есть единая строка diff diffstr
, представляющая разницу между oldstr
и newstr
:
# recreate `newstr` from `oldstr`+patch
newstr = apply_patch(oldstr, diffstr)
# recreate `oldstr` from `newstr`+patch
oldstr = apply_patch(newstr, diffstr, True)
В Python вы можете создать унифицированный diff двух строк, используя difflib (часть стандартной библиотеки):
import difflib
_no_eol = "\ No newline at end of file"
def make_patch(a,b):
"""
Get unified string diff between two strings. Trims top two lines.
Returns empty string if strings are identical.
"""
diffs = difflib.unified_diff(a.splitlines(True),b.splitlines(True),n=0)
try: _,_ = next(diffs),next(diffs)
except StopIteration: pass
return ''.join([d if d[-1] == '\n' else d+'\n'+_no_eol+'\n' for d in diffs])
В unix: diff -U0 a.txt b.txt
Код находится на GitHub здесь вместе с тестами с использованием ASCII и случайных символов юникода: https://gist.github.com/noporpoise/16e731849eb1231e86d78f9dfeca3abc
Ответ 6
Возможно, вы можете использовать unified_diff, чтобы создать список различий в файле. Только измененные тексты в вашем файле могут быть записаны в новый текстовый файл, где вы можете использовать его для вашей будущей ссылки.
Это код, который поможет вам написать только разницу в ваш новый файл.
Надеюсь, это то, о чем вы просите!
diff = difflib.unified_diff(old_file, new_file, lineterm='')
lines = list(diff)[2:]
# linesT = list(diff)[0:3]
print (lines[0])
added = [lineA for lineA in lines if lineA[0] == '+']
with open("output.txt", "w") as fh1:
for line in added:
fh1.write(line)
print '+',added
removed = [lineB for lineB in lines if lineB[0] == '-']
with open("output.txt", "a") as fh1:
for line in removed:
fh1.write(line)
print '-',removed
Используйте это в своем коде, чтобы сохранить только разностный выход!