4

私は、変更管理システムが行うと信じているようなことをしようとしていました.2つのファイルを比較し、ファイルが変更されるたびに小さな差分を保存します。私はこのページを読んでいます: http://docs.python.org/library/difflib.htmlそして、どうやら私の頭に沈んでいません。

以下に示すやや単純なプログラムでこれを再作成しようとしましたが、欠落しているように見えるのは、デルタに少なくとも元のファイルと同じくらい多くのものが含まれていることです。

純粋な変更だけに到達することはできませんか? 私が質問する理由は明らかです。ディスク容量を節約するためです。
毎回コードのチャンク全体を保存することもできますが、現在のコードを一度保存​​してから、変更の小さな差分を保存する方がよいでしょう。

また、多くの 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"
  • print "j=" + j

    「XYZ」を印刷

--- 5,9 ----

 print "HIJ"

 print "JKL"

 print "Hello World"

 print "XYZ"
  • 「終わり」を印刷する

別の更新:

メインフレーム用のソース管理ツールである Panvalet an Librarian の昔は、次のような変更セットを作成できました。

++ADD 9
   print "j=" + j 

これは単純に、9 行目の後に 1 行 (複数行) を追加することを意味します。次に、++REPLACE や ++UPDATE などの単語を追加します。 http://www4.hawaii.gov/dags/icsd/ppmo/Stds_Web_Pages/pdf/it110401.pdf

4

3 に答える 3

5

また、多くの difflib 関数がリストではなくジェネレーターを返す理由を理解しようとしていますが、その利点は何ですか?

ちょっと考えてみてください - ファイルを比較すると、それらのファイルは理論的には (そして実際にはそうなるでしょう) 非常に大きくなる可能性があります - たとえば、デルタをリストとして返すことは、完全なデータをメモリに読み込むことを意味します。賢明なことではありません。

差分のみを返すことに関しては、ジェネレーターを使用することには別の利点があります。デルタを反復処理し、関心のある行を保持するだけです。

Differ - スタイル デルタのdifflib ドキュメントを読むと、次のような段落が表示されます。

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を使用して、変更のみを示すコンパクト デルタを取得することもできます。

于 2011-01-20T04:33:52.987 に答える
4

差分には、バージョンを別のバージョンにパッチできるようにするのに十分な情報が含まれている必要があります。そのため、非常に小さなドキュメントへの単一行の変更の実験では、ドキュメント全体を保存する方が安くなる可能性があります。

ライブラリ関数はイテレータを返し、メモリが不足しているクライアントや、結果のシーケンスの一部のみを確認する必要があるクライアントで簡単に処理できるようにします。すべてのイテレータを非常に短いlist(an_iterator)式でリストに変換できるため、Python では問題ありません。

ほとんどの差分はテキストの行で行われますが、文字単位で行うことも可能difflibです。Differのオブジェクトのクラスを見てくださいdifflib

いたるところにある例は人間にわかりやすい出力を使用していますが、差分ははるかにコンパクトでコンピューターに適した方法で内部的に管理されています。また、diff には通常、パッチ適用とマージの変更を安全にするために冗長な情報 (削除する行のテキストなど) が含まれています。冗長性は、自分のコードで取り除くことができます。

difflib私は、最適性を支持して最小の驚きを選択することを読んだばかりです。これは、私が反対するものではありません。最小セットの変更を高速に生成するよく知られたアルゴリズムがあります。

私はかつて、約 1250 行の Java ( JRCS )で最適なアルゴリズムの 1 つと共に汎用差分エンジンをコーディングしました。等しいかどうかを比較できる要素のシーケンスに対して機能します。独自のソリューションを構築したい場合は、JRCS の翻訳/再実装にかかる Python の行数は 300 行以内にする必要があると思います。

によって生成された出力を処理して、difflibよりコンパクトにすることもオプションです。これは、3 つの変更 (追加、変更、および削除) を含む小さなファイルの例です。

---  
+++  
@@ -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
于 2011-01-20T05:04:50.943 に答える
1

変更だけが必要な場合は、統合またはコンテキスト diff を使用します。共通の行が含まれているため、より大きなファイルが表示されます。

ジェネレーターを返す利点は、一度にすべてをメモリに保持する必要がないことです。これは、非常に大きなファイルの比較に役立ちます。

于 2011-01-20T04:28:48.637 に答える