python difflib deltasとndiffを比較します
質問
私は、Change Control Systemsが行っていると思うこと、2つのファイルを比較し、ファイルが変更されるたびに小さなDiffを保存するようなことをしたいと思っていました。私はこのページを読んでいます: http://docs.python.org/library/difflib.html そして、それはどうやら私の頭に沈んでいません。
私はこれを以下に示すやや単純なプログラムでこれを再現しようとしていましたが、私が欠けているように見えることは、デルタには少なくとも元のファイルと同じくらい多く含まれているということです。
純粋な変化だけに到達することはできませんか?私が尋ねる理由は、ディスクスペースを保存するために、うまくいけば明白です。
毎回コード全体を保存することもできますが、現在のコードを1回保存してから、変更の小さな違いを保存する方が良いでしょう。
また、多くの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"
"j =" + jを印刷します
「XYZ」を印刷する
--- 5,9 ----
print "HIJ" print "JKL" print "Hello World" print "XYZ"
- 印刷「終わり」
別のアップデート:
Panvaletの昔は、メインフレームの司書であるソース管理ツールである昔は、次のようなChangesetを作成できます。
++ADD 9
print "j=" + j
これは、単に9行目の後に行(または行)を追加することを意味します。次に、++交換または++の更新などの単語の単語があります。http://www4.hawaii.gov/dags/icsd/ppmo/stds_web_pages/pdf/it110401.pdf
解決
DIFには、バージョンを別のバージョンにパッチすることができるようにするのに十分な情報を含める必要があります。はい、非常に小さなドキュメントへのシングルラインの変更の実験のために、ドキュメント全体を保存するのは安価です。
ライブラリ関数は、メモリがきつくなっているか、結果のシーケンスの一部を見る必要があるクライアントを簡単にするために、Iteratorを返します。すべてのイテレーターを非常に短いものでリストに変換できるため、Pythonでは大丈夫です list(an_iterator)
表現。
ほとんどの違いはテキストの行で行われますが、char-by-charに行くことは可能です。 difflib
それをします。を見てください Differ
オブジェクトのクラス difflib
.
あちこちの例は、人間に優しい出力を使用しますが、DIFFははるかにコンピュータでコンピューターに優しい方法で内部で管理されています。また、DIFFには通常、パッチを安全にしてマージするための冗長な情報(削除する行のテキストなど)が含まれます。あなたがそれに慣れていると感じるならば、あなた自身のコードによって冗長性を削除することができます。
私はそれを読んだばかりです difflib
最適性を支持して、最小限を選択してください。これは私が反論しないものです。がある よく知られています 最小限の変更セットを生成するのが速いアルゴリズム。
私はかつて、約1250行のJavaで最適なアルゴリズムの1つと一緒にジェネリックディフングエンジンをコーディングしました(JRCS)。平等と比較できる一連の要素に対して機能します。独自のソリューションを構築したい場合は、JRCの翻訳/再実装には300行以下のPythonが必要だと思います。
生成された出力の処理 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
他のヒント
また、多くのdifflib関数がリストの代わりにジェネレーターを返す理由をまだ把握しようとしていますが、そこでの利点は何ですか?
さて、それについて少し考えてみてください - ファイルを比較すると、これらのファイルが理論的に(そして実際には非常に大きくなる可能性があります) - デルタをリストとして返します。やるべきことではありません。
違いを返すだけで、ジェネレーターを使用することには別の利点があります。デルタを繰り返して、興味のあるラインを維持してください。
あなたが読んだら difflibドキュメント Differ -Style Deltasの場合、次のような段落が表示されます。
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
変更のみを示すコンパクトなデルタを取得するため。
変更が必要な場合は、UnifiedまたはContext Diffを使用します。共通の行が含まれているため、より大きなファイルが表示されます。
ジェネレーターを返すことの利点は、すべてが一度にメモリに保持される必要がないことです。これは、非常に大きなファイルを拡張するのに役立ちます。