Python Difflib delta和比较Ndiff

Python Difflib delta和比较Ndiff,python,delta,difflib,Python,Delta,Difflib,我想做一些我相信变更控制系统会做的事情,它们会比较两个文件,并在每次文件更改时保存一个小差异。 我一直在读这一页:显然它并没有深入我的脑海 我试着用一个简单的程序来重新创建它,如下所示, 但我似乎遗漏了一点,那就是Delta包含的内容至少和原始文件一样多,甚至更多 难道不可能实现纯粹的改变吗? 我提出这一要求的原因很明显——为了节省磁盘空间。 我可以每次只保存整个代码块,但最好只保存当前代码一次,然后保存少量更改 我还在试图弄清楚为什么许多difflib函数返回一个生成器而不是一个列表,这有什么

我想做一些我相信变更控制系统会做的事情,它们会比较两个文件,并在每次文件更改时保存一个小差异。 我一直在读这一页:显然它并没有深入我的脑海

我试着用一个简单的程序来重新创建它,如下所示, 但我似乎遗漏了一点,那就是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
-----显示上下文差异-----




*5,9**

 print "HIJ"

 print "JKL"

 print "Hello World"
  • 打印“j=”+j

    打印“XYZ”

---5,9----

  • 打印“结束”
另一个更新:

在Panvalet(图书馆员,大型机的源代码管理工具)的旧时代,您可以创建如下更改集:

++ADD 9
   print "j=" + j 
意思是在第9行之后加一行。 然后有像++替换或++更新这样的单词。

如果您只想更改,您需要使用统一或上下文差异。你会看到更大的文件,因为它包含了它们共同的行

返回一个生成器的好处是,整个东西不需要立即保存在内存中。这对于扩散非常大的文件非常有用

我还想弄清楚 为什么许多difflib函数返回 生成器而不是列表,是什么 优势在哪里

好吧,想一想——如果你比较文件,这些文件在理论上(在实践中)可能相当大——以列表形式返回增量,例如,意味着将完整的数据读入内存,这不是一件明智的事情

至于只返回差分,那么,使用生成器还有另一个优势——只需迭代delta并保留您感兴趣的任何行

如果您阅读了“不同样式的增量”,您将看到一段内容如下:

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
因此,如果您只需要差异,可以使用


您还可以使用
difflib.context\u diff
获得仅显示更改的紧凑增量。

diff必须包含足够的信息,以便能够将一个版本修补到另一个版本,因此,对于您对非常小的文档进行单行更改的实验,存储整个文档可能更便宜

库函数返回迭代器,以便在内存紧张或只需要查看结果序列的一部分的客户机上使用。在Python中这是可以的,因为每个迭代器都可以转换成一个带有非常短的
list(一个_迭代器)
表达式的列表

大多数差异都是在文本行上进行的,但是可以一个字符一个字符地进行差异,而
difflib
则可以这样做。查看
difflib
中的对象类

各地的例子都使用人性化的输出,但是差异是以一种更加紧凑、计算机友好的方式在内部管理的。此外,差异通常包含冗余信息(如要删除的行的文本),以确保修补和合并更改的安全性。如果您觉得舒服的话,可以通过自己的代码删除冗余

我刚刚读到,
difflib
选择了最优性,这是我不会反对的。有一些算法能够快速地产生一组最小的变化

我曾经在大约1250行Java()中编写了一个通用差分引擎和一个最佳算法。它适用于任何可以比较相等的元素序列。如果您想构建自己的解决方案,我认为JRC的翻译/重新实现应该不超过300行Python

处理
difflib
生成的输出以使其更紧凑也是一种选择。这是一个小文件的示例,其中包含三个更改(添加、更改和删除):

补丁所说的内容可以很容易地浓缩为:

+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

谢谢,我仍然对context_diff大于file2感到震惊。diff似乎会用它想要使用的任何内部语法说“add'print”j=“+j”at position 235”。您能否从上下文中进行.restore_diff?谢谢(请参阅上文@Karl的更新和注释)。我可能会处理一些简短的代码示例——通常是10-20行——所以我想我最好放弃diff的概念。如何将差异存储在数据库blob中?序列化它,或执行一个操作。这一切取决于目的。为什么要比较这些小文档?有多少文件?每个版本有多少修订?如果您控制了差分引擎,那么存储格式可以比一般情况下的任何修订版本更加紧凑(想想快速排序)。如果您希望在某些时间点使用/显示差异,那么对于小型文档,您可以在需要时使用
difflib
动态计算它们。一定要让我知道你的决定(我需要一个
-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