Python Difflib Deltas и сравнить Ndiff - PullRequest
4 голосов
/ 20 января 2011

Я искал что-то вроде того, что, как я считаю, делают системы управления изменениями, они сравнивают два файла и сохраняют небольшую разницу при каждом изменении файла.Я читал эту страницу: http://docs.python.org/library/difflib.html, и она, очевидно, не погружается в мою голову.

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

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

Я также все еще пытаюсь выяснить, почему многие функции difflib возвращают генератор вместо списка, в чем здесь преимущество?

Будет ли работать difflib для меня - или мне нужно найти более профессиональный пакет с большим количеством функций?

# Python Difflib demo 
# Author: Neal Walters 
# loosely based on http://ahlawat.net/wordpress/?p=371
# 01/17/2011 

# build the files here - later we will just read the files probably 
file1Contents="""
for j = 1 to 10: 
   print "ABC"
   print "DEF" 
   print "HIJ"
   print "JKL"
   print "Hello World"
   print "j=" + j 
   print "XYZ"
"""

file2Contents = """
for j = 1 to 10: 
   print "ABC"
   print "DEF" 
   print "HIJ"
   print "JKL"
   print "Hello World"
   print "XYZ"
print "The end"
"""

filename1 = "diff_file1.txt" 
filename2 = "diff_file2.txt" 

file1 = open(filename1,"w") 
file2 = open(filename2,"w") 

file1.write(file1Contents) 
file2.write(file2Contents) 

file1.close()
file2.close() 
#end of file build 

lines1 = open(filename1, "r").readlines()
lines2 = open(filename2, "r").readlines()

import difflib

print "\n FILE 1 \n" 
for line in lines1:
  print line 

print "\n FILE 2 \n" 
for line in lines2: 
  print line 

diffSequence = difflib.ndiff(lines1, lines2) 

print "\n ----- SHOW DIFF ----- \n" 
for i, line in enumerate(diffSequence):
    print line

diffObj = difflib.Differ() 
deltaSequence = diffObj.compare(lines1, lines2) 
deltaList = list(deltaSequence) 

print "\n ----- SHOW DELTALIST ----- \n" 
for i, line in enumerate(deltaList):
    print line



#let's suppose we store just the diffSequence in the database 
#then we want to take the current file (file2) and recreate the original (file1) from it
#by backward applying the diff 

restoredFile1Lines = difflib.restore(diffSequence,1)  # 1 indicates file1 of 2 used to create the diff 

restoreFileList = list(restoredFile1Lines)

print "\n ----- SHOW REBUILD OF FILE1 ----- \n" 
# this is not showing anything! 
for i, line in enumerate(restoreFileList): 
    print line

Спасибо!

ОБНОВЛЕНИЕ:

contextDiffSeq = difflib.context_diff(lines1, lines2) 
contextDiffList = list(contextDiffSeq) 

print "\n ----- SHOW CONTEXTDIFF ----- \n" 
for i, line in enumerate(contextDiffList):
    print line

----- ПОКАЗАТЬ CONTEXTDIFF -----




* 5,9 **

 print "HIJ"

 print "JKL"

 print "Hello World"
  • print "j =" + j

    print "XYZ "

--- 5,9 ----

 print "HIJ"

 print "JKL"

 print "Hello World"

 print "XYZ"
  • печать" Конец "

Еще одно обновление:

В старые времена библиотекаря Panvalet, инструментов управления исходным кодом для мэйнфрейма, вы могли создать набор изменений, подобный этому:

++ADD 9
   print "j=" + j 

Что просто означает добавление строки (или строк) после строки 9. Затем есть такие слова, как ++ REPLACE или ++ UPDATE.http://www4.hawaii.gov/dags/icsd/ppmo/Stds_Web_Pages/pdf/it110401.pdf

Ответы [ 3 ]

5 голосов
/ 20 января 2011

Я тоже все еще пытаюсь понять, почему многие функции difflib возвращают генератор вместо списка, что преимущество там?

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

Что касается только возврата разницы, то есть еще одно преимущество в использовании генератора - просто переберите дельту и сохраните все интересующие вас строки.

Если вы прочитаете документацию difflib для различий в стиле Differ, вы увидите параграф, который гласит:

Each line of a Differ delta begins with a two-letter code:
Code    Meaning
'- '    line unique to sequence 1
'+ '    line unique to sequence 2
'  '    line common to both sequences
'? '    line not present in either input sequence

Итак, если вам нужны только различия, вы можете легко отфильтровать их, используя str.startswith

Вы также можете использовать difflib.context_diff для получения компактной дельты, которая показывает только изменения.

4 голосов
/ 20 января 2011

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

Библиотечные функции возвращают итераторы, чтобы облегчить работу с клиентами, которые испытывают нехватку памяти или должны просматривать только часть полученной последовательности.Это нормально в Python, потому что каждый итератор может быть преобразован в список с очень коротким list(an_iterator) выражением.

Большая часть различий выполняется по строкам текста, но можно перейти к символу-по-порядку.char, и difflib делает это.Взгляните на Differ класс объектов в difflib.

В примерах повсюду используются удобные для человека результаты, но различия управляются изнутри гораздо более компактным и удобным для компьютера способом.Кроме того, различия обычно содержат избыточную информацию (например, текст удаляемой строки), чтобы сделать исправления и объединить изменения безопасными.Избыточность может быть удалена вашим собственным кодом, если вам это удобно.

Я только что прочитал, что difflib выбирает наименьшее удивление в пользу оптимальности, с чем я не буду спорить.Существуют хорошо известные алгоритмы, которые быстро производят минимальный набор изменений.

Однажды я кодировал универсальный механизм вычисления и один из оптимальных алгоритмов в 1250 строках Java ( JRCS ).Это работает для любой последовательности элементов, которые можно сравнить на равенство.Если вы хотите создать собственное решение, я думаю, что перевод / переопределение JRCS должно занять не более 300 строк Python.

Также возможна обработка выходных данных, создаваемых difflib, чтобы сделать его более компактным.Это пример небольших файлов с тремя изменениями (добавление, изменение и удаление):

---  
+++  
@@ -7,0 +7,1 @@
+aaaaa
@@ -9,1 +10,1 @@
-c= 0
+c= 1
@@ -15,1 +16,0 @@
-    m = re.match(code_re, text)

То, что говорится в патче, может быть легко сжато:

+7,1 
aaaaa
-9,1 
+10,1
c= 1
-15,1

Для вашего собственного примера сжатый вывод будет выглядеть следующим образом:

-8,1
+9,1
print "The end"

В целях безопасности оставление в начале маркера ('>') для строк, которые должны быть вставлены, может быть хорошей идеей.

-8,1
+9,1
>print "The end"

Это ближе к тому, что вам нужно?

Это простая функция для уплотнения.Вам придется написать собственный код, чтобы применить патч в этом формате, но он должен быть простым.

def compact_a_unidiff(s):
    s = [l for l in s if l[0] in ('+','@')]
    result = []
    for l in s:
        if l.startswith('++'):
            continue
        elif l.startswith('+'):
            result.append('>'+ l[1:])
        else:
            del_cmd, add_cmd = l[3:-3].split()
            del_pair, add_pair = (c.split(',') for c in (del_cmd,add_cmd))
            if del_pair[1]  != '0':
                result.append(del_cmd)
            if add_pair[1] != '0':
                result.append(add_cmd)
    return result
1 голос
/ 20 января 2011

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

Преимущество возврата генератора состоит в том, что все это не нужно хранить в памяти сразу.Это может быть полезно для просмотра очень больших файлов.

...