2011-01-20 20 views
3

我正在尋找像我認爲改變控制系統一樣的事情,他們比較兩個文件,並且每次文件改變時保存一個小差異。 我一直在閱讀這個頁面:http://docs.python.org/library/difflib.html,它並沒有沉入我的腦海。Python Difflib Deltas和Compare Ndiff

我試圖在下面顯示的一個簡單的程序中重新創建這個, 但我似乎缺少的東西是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 

謝謝!

UPDATE:

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

回答

4

的diff必須包含足夠的信息,以使其能夠修補版本到另一個,所以是的,你的單行變化非常小的文件,保存整個文件的實驗可能會更便宜。

庫函數返回迭代器,以便在內存緊張的客戶端上更簡單,或者只需查看部分結果序列。在Python中是可以的,因爲每個迭代器都可以被轉換爲一個表達式爲list(an_iterator)的列表。

大多數差異是在文本行上完成的,但是可以逐字逐句地進行,並且difflib這樣做。看看difflibDiffer類的對象。

遍佈各地的例子使用人性化的輸出,但差異在內部以一種更加緊湊,計算機友好的方式進行內部管理。此外,差異通常包含冗餘信息(如要刪除的行的文本)以使修補和合並更改安全。如果您對此感到滿意,可以通過您自己的代碼刪除冗餘。

我剛纔讀的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 
+0

謝謝(請參閱上面的@Karl更新和註釋)。我可能會處理短代碼樣本 - 一般10-20行 - 所以我想我最好放棄差異概念。你將如何將差異存儲在數據庫blob中?序列化它,或者做一個''.join – NealWalters 2011-01-20 05:19:37

1

如果您只是想要更改,您希望使用統一或上下文差異。你看到更大的文件,因爲它包含了它們共同的行。

返回一個生成器的好處是整個事情不需要一次保存在內存中。這可以用於區分非常大的文件。

+0

謝謝,我仍然震驚的是,context_diff大於file2。似乎diff會說出類似於「添加」打印「位置235處的j =」+ j'的東西,不管它想要使用什麼內部語法。你可以從context_diff做一個.restore嗎? – NealWalters 2011-01-20 05:16:34

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

您還可以使用過濾那些出3210以獲得僅顯示變化的緊湊增量。