我希望做一些我认为改变控制系统做的事情,他们比较两个文件,并在每次文件更改时保存一个小差异。 我一直在读这页: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
----- SHOW CONTEXTDIFF -----
* 5,9 **
print "HIJ" print "JKL" print "Hello World"
打印“j =”+ j
打印“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
答案 0 :(得分:4)
我还在努力弄明白 为什么许多difflib函数返回一个 生成器而不是列表,是什么 有优势吗?
好吧,想想它一秒钟 - 如果你比较文件,那些理论上(并且将在实践中)的文件可以非常大 - 将delta作为列表返回,例如,意味着将完整的数据读入内存,这不是一件好事。
至于只返回差异,那么使用生成器还有另一个优点 - 只需迭代三角形并保留您感兴趣的任何行。
如果你阅读difflib documentation的不同风格的增量,你会看到一个段落:
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
获取仅显示更改的紧凑增量。
答案 1 :(得分:4)
Diffs必须包含足够的信息才能将版本修补到另一个版本中,所以是的,对于您对单个行更改为非常小的文档的实验,存储整个文档可能会更便宜。
库函数返回迭代器,以便在内存紧张或只需要查看结果序列的一部分的客户端上更容易。它在Python中没问题,因为每个迭代器都可以转换为具有非常短的list(an_iterator)
表达式的列表。
大多数差异是在文本行上完成的,但可以归结为char-by-char,而difflib
可以执行此操作。查看difflib
中的Differ
类对象。
这个地方的例子都使用了人性化的输出,但差异是在内部以更紧凑,计算机友好的方式管理的。此外,差异通常包含冗余信息(如要删除的行的文本),以使安装修补和合并更改。如果您对此感到满意,可以使用您自己的代码删除冗余。
我刚刚读到difflib
选择最少的意外,支持最优性,这是我不会反对的。有well known个算法可以快速生成最小的更改集。
我曾经在大约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
答案 2 :(得分:1)
如果您只是想要更改,则需要使用统一或上下文差异。你看到的是更大的文件,因为它包含了它们的共同点。
返回生成器的优点是整个事物不需要立即保存在内存中。这对于区分非常大的文件非常有用。