此模塊提供用于比較序列的類和函數。 例如,它可被用于比較文件,并可產生多種格式的不同文件差異信息,包括 HTML 和上下文以及統一的 diff 數據。 有關比較目錄和文件,另請參閱?filecmp?模塊。
class?difflib.SequenceMatcher
這是一個靈活的類,可用于比較任何類型的序列對,只要序列元素為?hashable?對象。 其基本算法要早于由 Ratcliff 和 Obershelp 于 1980 年代末期發表并以“格式塔模式匹配”的夸張名稱命名的算法,并且更加有趣一些。 其思路是找到不包含“垃圾”元素的最長連續匹配子序列;所謂“垃圾”元素是指其在某種意義上沒有價值,例如空白行或空白符。 (處理垃圾元素是對 Ratcliff 和 Obershelp 算法的一個擴展。) 然后同樣的思路將遞歸地應用于匹配序列的左右序列片段。 這并不能產生最小編輯序列,但確實能產生在人們看來“正確”的匹配。
耗時:?基本 Ratcliff-Obershelp 算法在最壞情況下為立方時間而在一般情況下為平方時間。?SequenceMatcher?在最壞情況下為平方時間而在一般情況下的行為受到序列中有多少相同元素這一因素的微妙影響;在最佳情況下則為線性時間。
自動垃圾啟發式計算:?SequenceMatcher?支持使用啟發式計算來自動將特定序列項視為垃圾。 這種啟發式計算會統計每個單獨項在序列中出現的次數。 如果某一項(在第一項之后)的重復次數超過序列長度的 1% 并且序列長度至少有 200 項,該項會被標記為“熱門”并被視為序列匹配中的垃圾。 這種啟發式計算可以通過在創建?SequenceMatcher?時將?autojunk
?參數設為?False
?來關閉。
在 3.2 版本發生變更:?增加了?autojunk?形參。
class?difflib.Differ
這個類的作用是比較由文本行組成的序列,并產生可供人閱讀的差異或增量信息。 Differ 統一使用?SequenceMatcher?來完成行序列的比較以及相似(接近匹配)行內部字符序列的比較。
Differ?增量的每一行均以雙字母代碼打頭:
雙字母代碼 | 含意 |
---|---|
| 行為序列 1 所獨有 |
| 行為序列 2 所獨有 |
| 行在兩序列中相同 |
| 行不存在于任一輸入序列 |
以 '?
' 打頭的行嘗試將視線紖至行以外而不存在于任一輸入序列的差異。 如果序列包含空白符,例如空格、制表或換行則這些行可能會令人感到迷惑。
class?difflib.HtmlDiff
這個類可用于創建 HTML 表格(或包含表格的完整 HTML 文件)以并排地逐行顯示文本比較,行間與行外的更改將突出顯示。 此表格可以基于完全或上下文差異模式來生成。
這個類的構造函數:
__init__(tabsize=8,?wrapcolumn=None,?linejunk=None,?charjunk=IS_CHARACTER_JUNK)
初始化?HtmlDiff?的實例。
tabsize?是一個可選關鍵字參數,指定制表位的間隔,默認值為?8
。
wrapcolumn?是一個可選關鍵字參數,指定行文本自動打斷并換行的列位置,默認值為?None
?表示不自動換行。
linejunk?和?charjunk?均是可選關鍵字參數,會傳入?ndiff()?(被?HtmlDiff?用來生成并排顯示的 HTML 差異)。 請參閱?ndiff()?文檔了解參數默認值及其說明。
下列是公開的方法
make_file(fromlines,?tolines,?fromdesc='',?todesc='',?context=False,?numlines=5,?*,?charset='utf-8')
比較?fromlines?和?tolines?(字符串列表) 并返回一個字符串,表示一個完整 HTML 文件,其中包含各行差異的表格,行間與行外的更改將突出顯示。
fromdesc?和?todesc?均是可選關鍵字參數,指定來源/目標文件的列標題字符串(默認均為空白字符串)。
context?和?numlines?均是可選關鍵字參數。 當只要顯示上下文差異時就將?context?設為?True
,否則默認值?False
?為顯示完整文件。?numlines?默認為?5
。 當?context?為?True
?時?numlines?將控制圍繞突出顯示差異部分的上下文行數。 當?context?為?False
?時?numlines?將控制在使用 "next" 超鏈接時突出顯示差異部分之前所顯示的行數(設為零則會導致 "next" 超鏈接將下一個突出顯示差異部分放在瀏覽器頂端,不添加任何前導上下文)。
備注
fromdesc?和?todesc?會被當作未轉義的 HTML 來解讀,當接收不可信來源的輸入時應該適當地進行轉義。
在 3.5 版本發生變更:?增加了?charset?關鍵字參數。 HTML 文檔的默認字符集從?'ISO-8859-1'
?更改為?'utf-8'
。
make_table(fromlines,?tolines,?fromdesc='',?todesc='',?context=False,?numlines=5)
比較?fromlines?和?tolines?(字符串列表) 并返回一個字符串,表示一個包含各行差異的完整 HTML 表格,行間與行外的更改將突出顯示。
此方法的參數與?make_file()?方法的相同。
difflib.context_diff(a,?b,?fromfile='',?tofile='',?fromfiledate='',?tofiledate='',?n=3,?lineterm='\n')
比較?a?和?b?(字符串列表);返回上下文差異格式的增量信息 (一個產生增量行的?generator)。
所謂上下文差異是一種只顯示有更改的行再加幾個上下文行的緊湊形式。 更改被顯示為之前/之后的樣式。 上下文行數由?n?設定,默認為三行。
默認情況下,差異控制行(以?***
?or?---
?表示)是通過末尾換行符來創建的。 這樣做的好處是從?io.IOBase.readlines()?創建的輸入將得到適用于?io.IOBase.writelines()?的差異信息,因為輸入和輸出都帶有末尾換行符。
對于沒有末尾換行符的輸入,應將?lineterm?參數設為?""
,這樣輸出內容將統一不帶換行符。
上下文差異格式通常帶有一個記錄文件名和修改時間的標頭。 這些信息的部分或全部可以使用字符串?fromfile,?tofile,?fromfiledate?和?tofiledate?來指定。 修改時間通常以 ISO 8601 格式表示。 如果未指定,這些字符串默認為空。
>>>
>>> import sys >>> from difflib import * >>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n'] >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n'] >>> sys.stdout.writelines(context_diff(s1, s2, fromfile='before.py', ... tofile='after.py')) *** before.py --- after.py *************** *** 1,4 **** ! bacon ! eggs ! hamguido --- 1,4 ---- ! python ! eggy ! hamsterguido
請參閱?difflib 的命令行接口?獲取更詳細的示例。
difflib.get_close_matches(word,?possibilities,?n=3,?cutoff=0.6)
返回由最佳“近似”匹配構成的列表。?word?為一個指定目標近似匹配的序列(通常為字符串),possibilities?為一個由用于匹配?word?的序列構成的列表(通常為字符串列表)。
可選參數?n?(默認為?3
) 指定最多返回多少個近似匹配;?n?必須大于?0
.
可選參數?cutoff?(默認為?0.6
) 是一個 [0, 1] 范圍內的浮點數。 與?word?相似度得分未達到該值的候選匹配將被忽略。
候選匹配中(不超過?n?個)的最佳匹配將以列表形式返回,按相似度得分排序,最相似的排在最前面。
>>>
>>> get_close_matches('appel', ['ape', 'apple', 'peach', 'puppy']) ['apple', 'ape'] >>> import keyword >>> get_close_matches('wheel', keyword.kwlist) ['while'] >>> get_close_matches('pineapple', keyword.kwlist) [] >>> get_close_matches('accept', keyword.kwlist) ['except']
difflib.ndiff(a,?b,?linejunk=None,?charjunk=IS_CHARACTER_JUNK)
比較?a?和?b?(字符串列表);返回?Differ?形式的增量信息 (一個產生增量行的?generator)。
可選關鍵字形參?linejunk?和?charjunk?均為過濾函數 (或為?None
):
linejunk: 此函數接受單個字符串參數,如果其為垃圾字符串則返回真值,否則返回假值。 默認為?None
。 此外還有一個模塊層級的函數?IS_LINE_JUNK(),它會過濾掉沒有可見字符的行,除非該行添加了至多一個井號符 ('#'
) -- 但是下層的?SequenceMatcher?類會動態分析哪些行的重復頻繁到足以形成噪音,這通常會比使用此函數的效果更好。
charjunk: 此函數接受一個字符(長度為 1 的字符串),如果其為垃圾字符則返回真值,否則返回假值。 默認為模塊層級的函數?IS_CHARACTER_JUNK(),它會過濾掉空白字符(空格符或制表符;但包含換行符可不是個好主意!)。
>>>
>>> diff = ndiff('one\ntwo\nthree\n'.splitlines(keepends=True), ... 'ore\ntree\nemu\n'.splitlines(keepends=True)) >>> print(''.join(diff), end="") - one ? ^ + ore ? ^ - two - three ? - + tree + emu
difflib.restore(sequence,?which)
返回兩個序列中產生增量的那一個。
給出一個由?Differ.compare()?或?ndiff()?產生的?序列,提取出來自文件 1 或 2 (which?形參) 的行,去除行前綴。
示例:
>>>
>>> diff = ndiff('one\ntwo\nthree\n'.splitlines(keepends=True), ... 'ore\ntree\nemu\n'.splitlines(keepends=True)) >>> diff = list(diff) # materialize the generated delta into a list >>> print(''.join(restore(diff, 1)), end="") one two three >>> print(''.join(restore(diff, 2)), end="") ore tree emu
difflib.unified_diff(a,?b,?fromfile='',?tofile='',?fromfiledate='',?tofiledate='',?n=3,?lineterm='\n')
比較?a?和?b?(字符串列表);返回統一差異格式的增量信息 (一個產生增量行的?generator)。
所以統一差異是一種只顯示有更改的行再加幾個上下文行的緊湊形式。 更改被顯示為內聯的樣式(而不是分開的之前/之后文本塊)。 上下文行數由?n?設定,默認為三行。
默認情況下,差異控制行 (以?---
,?+++
?或?@@
?表示) 是通過末尾換行符來創建的。 這樣做的好處是從?io.IOBase.readlines()?創建的輸入將得到適用于?io.IOBase.writelines()?的差異信息,因為輸入和輸出都帶有末尾換行符。
對于沒有末尾換行符的輸入,應將?lineterm?參數設為?""
,這樣輸出內容將統一不帶換行符。
統一的差異格式通常帶有一個記錄文件名和修改時間的標頭。 這些信息的部分或全部可以使用字符串?fromfile,?tofile,?fromfiledate?和?tofiledate?來指定。 修改時間通常以 ISO 8601 格式表示。 如果未指定,這些字符串將默認為空。
>>>
>>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n'] >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n'] >>> sys.stdout.writelines(unified_diff(s1, s2, fromfile='before.py', tofile='after.py')) --- before.py +++ after.py @@ -1,4 +1,4 @@ -bacon -eggs -ham +python +eggy +hamsterguido
請參閱?difflib 的命令行接口?獲取更詳細的示例。
difflib.diff_bytes(dfunc,?a,?b,?fromfile=b'',?tofile=b'',?fromfiledate=b'',?tofiledate=b'',?n=3,?lineterm=b'\n')
使用?dfunc?比較?a?和?b?(字節串對象列表);產生以?dfunc?所返回格式表示的差異行列表(也是字節串)。?dfunc?必須是可調用對象,通常為?unified_diff()?或?context_diff()。
允許你比較編碼未知或不一致的數據。 除?n?之外的所有輸入都必須為字節串對象而非字符串。 作用方式為無損地將所有輸入 (除?n?之外) 轉換為字符串,并調用?dfunc(a,?b,?fromfile,?tofile,?fromfiledate,?tofiledate,?n,?lineterm)
。?dfunc?的輸出會被隨即轉換回字節串,這樣你所得到的增量行將具有與?a?和?b?相同的未知/不一致編碼。
在 3.5 版本加入.
difflib.IS_LINE_JUNK(line)
對于可忽略的行返回?True
。 如果?line?為空行或只包含單個?'#'
?則?line?行就是可忽略的,否則就是不可忽略的。 此函數被用作較舊版本?ndiff()?中?linejunk?形參的默認值。
difflib.IS_CHARACTER_JUNK(ch)
對于可忽略的字符返回?True
。 字符?ch?如果為空格符或制表符則?ch?就是可忽略的,否則就是不可忽略的。 此函數被用作?ndiff()?中?charjunk?形參的默認值。
參見
Pattern Matching: The Gestalt Approach
John W. Ratcliff 和 D. E. Metzener 對于一種類似算法的討論。 此文于 1988 年 7 月發表于?Dr. Dobb's Journal。
SequenceMatcher 對象
SequenceMatcher?類具有這樣的構造器:
class?difflib.SequenceMatcher(isjunk=None,?a='',?b='',?autojunk=True)
可選參數?isjunk?必須為?None
?(默認值) 或為接受一個序列元素并當且僅當其為應忽略的“垃圾”元素時返回真值的單參數函數。 傳入?None
?作為?isjunk?的值就相當于傳入?lambda?x:?False
;也就是說不忽略任何值。 例如,傳入:
lambda x: x in " \t"
如果你以字符序列的形式對行進行比較,并且不希望區分空格符或硬制表符。
可選參數?a?和?b?為要比較的序列;兩者默認為空字符串。 兩個序列的元素都必須為?hashable。
可選參數?autojunk?可用于啟用自動垃圾啟發式計算。
在 3.2 版本發生變更:?增加了?autojunk?形參。
SequenceMatcher 對象接受三個數據屬性:?bjunk?是?b?當中?isjunk?為?True
?的元素集合;bpopular?是被啟發式計算(如果其未被禁用)視為熱門候選的非垃圾元素集合;b2j?是將?b?當中剩余元素映射到一個它們出現位置列表的字典。 所有三個數據屬性將在?b?通過?set_seqs()?或?set_seq2()?重置時被重置。
在 3.2 版本加入:?bjunk?和?bpopular?屬性。
SequenceMatcher?對象具有以下方法:
set_seqs(a,?b)
設置要比較的兩個序列。
SequenceMatcher?計算并緩存有關第二個序列的詳細信息,這樣如果你想要將一個序列與多個序列進行比較,可使用?set_seq2()?一次性地設置該常用序列并重復地對每個其他序列各調用一次?set_seq1()。
set_seq1(a)
設置要比較的第一個序列。 要比較的第二個序列不會改變。
set_seq2(b)
設置要比較的第二個序列。 要比較的第一個序列不會改變。
find_longest_match(alo=0,?ahi=None,?blo=0,?bhi=None)
找出?a[alo:ahi]
?和?b[blo:bhi]
?中的最長匹配塊。
如果?isjunk?被省略或為?None
,find_longest_match()?將返回?(i,?j,?k)
?使得?a[i:i+k]
?等于?b[j:j+k]
,其中?alo?<=?i?<=?i+k?<=?ahi
?并且?blo?<=?j?<=?j+k?<=?bhi
。 對于所有滿足這些條件的?(i',?j',?k')
,如果?i?==?i'
,?j?<=?j'
?也被滿足,則附加條件?k?>=?k'
,?i?<=?i'
。 換句話說,對于所有最長匹配塊,返回在?a?當中最先出現的一個,而對于在?a?當中最先出現的所有最長匹配塊,則返回在?b?當中最先出現的一個。
>>>
>>> s = SequenceMatcher(None, " abcd", "abcd abcd") >>> s.find_longest_match(0, 5, 0, 9) Match(a=0, b=4, size=5)
如果提供了?isjunk,將按上述規則確定第一個最長匹配塊,但額外附加不允許塊內出現垃圾元素的限制。 然后將通過(僅)匹配兩邊的垃圾元素來盡可能地擴展該塊。 這樣結果塊絕對不會匹配垃圾元素,除非同樣的垃圾元素正好與有意義的匹配相鄰。
這是與之前相同的例子,但是將空格符視為垃圾。 這將防止?'?abcd'
?直接與第二個序列末尾的?'?abcd'
?相匹配。 而只可以匹配?'abcd'
,并且是匹配第二個序列最左邊的?'abcd'
:
>>>
>>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd") >>> s.find_longest_match(0, 5, 0, 9) Match(a=1, b=0, size=4)
如果未找到匹配塊,此方法將返回?(alo,?blo,?0)
。
此方法將返回一個?named tuple?Match(a,?b,?size)
。
在 3.9 版本發生變更:?加入默認參數。
get_matching_blocks()
返回描述非重疊匹配子序列的三元組列表。 每個三元組的形式為?(i,?j,?n)
,其含義為?a[i:i+n]?==?b[j:j+n]
。 這些三元組按?i?和?j?單調遞增排列。
最后一個三元組用于占位,其值為?(len(a),?len(b),?0)
。 它是唯一?n?==?0
?的三元組。 如果?(i,?j,?n)
?和?(i',?j',?n')
?是在列表中相鄰的三元組,且后者不是列表中的最后一個三元組,則?i+n?<?i'
?或?j+n?<?j'
;換句話說,相鄰的三元組總是描述非相鄰的相等塊。
>>>
>>> s = SequenceMatcher(None, "abxcd", "abcd") >>> s.get_matching_blocks() [Match(a=0, b=0, size=2), Match(a=3, b=2, size=2), Match(a=5, b=4, size=0)]
get_opcodes()
返回描述如何將?a?變為?b?的 5 元組列表,每個元組的形式為?(tag,?i1,?i2,?j1,?j2)
。 在第一個元組中?i1?==?j1?==?0
,而在其余的元組中?i1?等于前一個元組的?i2,并且?j1?也等于前一個元組的?j2。
tag?值為字符串,其含義如下:
值 | 含意 |
---|---|
|
|
|
|
|
|
|
|
例如:
>>>
>>> a = "qabxcd" >>> b = "abycdf" >>> s = SequenceMatcher(None, a, b) >>> for tag, i1, i2, j1, j2 in s.get_opcodes(): ... print('{:7} a[{}:{}] --> b[{}:{}] {!r:>8} --> {!r}'.format( ... tag, i1, i2, j1, j2, a[i1:i2], b[j1:j2])) delete a[0:1] --> b[0:0] 'q' --> '' equal a[1:3] --> b[0:2] 'ab' --> 'ab' replace a[3:4] --> b[2:3] 'x' --> 'y' equal a[4:6] --> b[3:5] 'cd' --> 'cd' insert a[6:6] --> b[5:6] '' --> 'f'
get_grouped_opcodes(n=3)
返回一個帶有最多?n?行上下文的分組的?generator。
從?get_opcodes()?所返回的組開始,此方法會拆分出較小的更改簇并消除沒有更改的間隔區域。
這些分組以與?get_opcodes()?相同的格式返回。
ratio()
返回一個取值范圍 [0, 1] 的浮點數作為序列相似性度量。
其中 T 是兩個序列中元素的總數量,M 是匹配的數量,即 2.0*M / T。 請注意如果兩個序列完全相同則該值為?1.0
,如果兩者完全不同則為?0.0
。
如果?get_matching_blocks()?或?get_opcodes()?尚未被調用則此方法運算消耗較大,在此情況下你可能需要先調用?quick_ratio()?或?real_quick_ratio()?來獲取一個上界。
備注
注意:?ratio()?調用的結果可能會取決于參數的順序。 例如:
>>>
>>> SequenceMatcher(None, 'tide', 'diet').ratio() 0.25 >>> SequenceMatcher(None, 'diet', 'tide').ratio() 0.5
quick_ratio()
相對快速地返回一個?ratio()?的上界。
real_quick_ratio()
非常快速地返回一個?ratio()?的上界。
這三個返回匹配部分點總字符數之比的三種方法可能由于不同的近似級別而給出不同的結果,但是?quick_ratio()?和?real_quick_ratio()?總是會至少與?ratio()?一樣大:
>>>
>>> s = SequenceMatcher(None, "abcd", "bcde") >>> s.ratio() 0.75 >>> s.quick_ratio() 0.75 >>> s.real_quick_ratio() 1.0
SequenceMatcher 的示例
以下示例比較兩個字符串,并將空格視為“垃圾”:
>>>
>>> s = SequenceMatcher(lambda x: x == " ", ... "private Thread currentThread;", ... "private volatile Thread currentThread;")
ratio()?返回一個 [0, 1] 范圍內的浮點數,用來衡量序列的相似度。 根據經驗,ratio()?值超過 0.6 就意味著兩個序列非常接近匹配:
>>>
>>> print(round(s.ratio(), 3)) 0.866
如果您只對序列的匹配的位置感興趣,則?get_matching_blocks()?就很方便:
>>>
>>> for block in s.get_matching_blocks(): ... print("a[%d] and b[%d] match for %d elements" % block) a[0] and b[0] match for 8 elements a[8] and b[17] match for 21 elements a[29] and b[38] match for 0 elements
請注意?get_matching_blocks()?返回的最后一個元組?(len(a),?len(b),?0)
?始終只用于占位,這也是元組的末尾元素(匹配的元素個數)為?0
?的唯一情況。
如果你想要知道如何將第一個序列轉成第二個序列,可以使用?get_opcodes():
>>>
>>> for opcode in s.get_opcodes(): ... print("%6s a[%d:%d] b[%d:%d]" % opcode)equal a[0:8] b[0:8] insert a[8:8] b[8:17]equal a[8:29] b[17:38]
參見
-
此模塊中的?get_close_matches()?函數顯示了如何基于?SequenceMatcher?構建簡單的代碼來執行有用的功能。
-
使用?SequenceMatcher?構建小型應用的?簡易版本控制方案。
Differ 對象
請注意?Differ?所生成的增量并不保證是?最小?差異。 相反,最小差異往往是違反直覺的,因為它們會同步任何可能的地方,有時甚至意外產生相距 100 頁的匹配。 將同步點限制為連續匹配保留了一些局部性概念,這偶爾會帶來產生更長差異的代價。
Differ?類具有這樣的構造器:
class?difflib.Differ(linejunk=None,?charjunk=None)
可選關鍵字形參?linejunk?和?charjunk?均為過濾函數 (或為?None
):
linejunk: 接受單個字符串作為參數的函數,如果其為垃圾字符串則返回真值。 默認值為?None
,意味著沒有任何行會被視為垃圾行。
charjunk: 接受單個字符(長度為 1 的字符串)作為參數的函數,如果其為垃圾字符則返回真值。 默認值為?None
,意味著沒有任何字符會被視為垃圾字符。
這些垃圾過濾函數可加快查找差異的匹配速度,并且不會導致任何差異行或字符被忽略。 請閱讀?find_longest_match()?方法的?isjunk?形參的描述了解詳情。
Differ?對象是通過一個單獨方法來使用(生成增量)的:
compare(a,?b)
比較兩個由行組成的序列,并生成增量(一個由行組成的序列)。
每個序列必須包含一個以換行符結尾的單行字符串。 這樣的序列可以通過文件型對象的?readlines()?方法來獲取。 所生成的增量同樣由以換行符結尾的字符串構成,可以通過文件型對象的?writelines()?方法原樣打印出來。
Differ 示例
此示例比較兩段文本。 首先我們設置文本為以換行符結尾的單行字符串組成的序列(這樣的序列也可以通過文件型對象的?readlines()?方法來獲取):
>>>
>>> text1 = ''' 1. Beautiful is better than ugly. ... 2. Explicit is better than implicit. ... 3. Simple is better than complex. ... 4. Complex is better than complicated. ... '''.splitlines(keepends=True) >>> len(text1) 4 >>> text1[0][-1] '\n' >>> text2 = ''' 1. Beautiful is better than ugly. ... 3. Simple is better than complex. ... 4. Complicated is better than complex. ... 5. Flat is better than nested. ... '''.splitlines(keepends=True)
接下來我們實例化一個 Differ 對象:
>>>
>>> d = Differ()
請注意在實例化?Differ?對象時我們可以傳入函數來過濾掉“垃圾”行和字符。 詳情參見?Differ()?構造器說明。
最后,我們比較兩個序列:
>>>
>>> result = list(d.compare(text1, text2))
result
?是一個字符串列表,讓我們將其美化打印出來:
>>>
>>> from pprint import pprint >>> pprint(result) [' 1. Beautiful is better than ugly.\n','- 2. Explicit is better than implicit.\n','- 3. Simple is better than complex.\n','+ 3. Simple is better than complex.\n','? ++\n','- 4. Complex is better than complicated.\n','? ^ ---- ^\n','+ 4. Complicated is better than complex.\n','? ++++ ^ ^\n','+ 5. Flat is better than nested.\n']
作為單獨的多行字符串顯示出來則是這樣:
>>>
>>> import sys >>> sys.stdout.writelines(result)1. Beautiful is better than ugly. - 2. Explicit is better than implicit. - 3. Simple is better than complex. + 3. Simple is better than complex. ? ++ - 4. Complex is better than complicated. ? ^ ---- ^ + 4. Complicated is better than complex. ? ++++ ^ ^ + 5. Flat is better than nested.
difflib 的命令行接口
這個例子演示了如何使用 difflib 來創建類似?diff
?的工具。
""" Command line interface to difflib.py providing diffs in four formats:* ndiff: lists every line and highlights interline changes. * context: highlights clusters of changes in a before/after format. * unified: highlights clusters of changes in an inline format. * html: generates side by side comparison with change highlights."""import sys, os, difflib, argparse from datetime import datetime, timezonedef file_mtime(path):t = datetime.fromtimestamp(os.stat(path).st_mtime,timezone.utc)return t.astimezone().isoformat()def main():parser = argparse.ArgumentParser()parser.add_argument('-c', action='store_true', default=False,help='Produce a context format diff (default)')parser.add_argument('-u', action='store_true', default=False,help='Produce a unified format diff')parser.add_argument('-m', action='store_true', default=False,help='Produce HTML side by side diff ''(can use -c and -l in conjunction)')parser.add_argument('-n', action='store_true', default=False,help='Produce a ndiff format diff')parser.add_argument('-l', '--lines', type=int, default=3,help='Set number of context lines (default 3)')parser.add_argument('fromfile')parser.add_argument('tofile')options = parser.parse_args()n = options.linesfromfile = options.fromfiletofile = options.tofilefromdate = file_mtime(fromfile)todate = file_mtime(tofile)with open(fromfile) as ff:fromlines = ff.readlines()with open(tofile) as tf:tolines = tf.readlines()if options.u:diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n)elif options.n:diff = difflib.ndiff(fromlines, tolines)elif options.m:diff = difflib.HtmlDiff().make_file(fromlines,tolines,fromfile,tofile,context=options.c,numlines=n)else:diff = difflib.context_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n)sys.stdout.writelines(diff)if __name__ == '__main__':main()
ndiff 示例
這個例子演示了如何使用?difflib.ndiff()。
"""ndiff [-q] file1 file2or ndiff (-r1 | -r2) < ndiff_output > file1_or_file2Print a human-friendly file difference report to stdout. Both inter- and intra-line differences are noted. In the second form, recreate file1 (-r1) or file2 (-r2) on stdout, from an ndiff report on stdin.In the first form, if -q ("quiet") is not specified, the first two lines of output are-: file1 +: file2Each remaining line begins with a two-letter code:"- " line unique to file1"+ " line unique to file2" " line common to both files"? " line not present in either input fileLines beginning with "? " attempt to guide the eye to intraline differences, and were not present in either input file. These lines can be confusing if the source files contain tab characters.The first file can be recovered by retaining only lines that begin with " " or "- ", and deleting those 2-character prefixes; use ndiff with -r1.The second file can be recovered similarly, but by retaining only " " and "+ " lines; use ndiff with -r2; or, on Unix, the second file can be recovered by piping the output throughsed -n '/^[+ ] /s/^..//p' """__version__ = 1, 7, 0import difflib, sysdef fail(msg):out = sys.stderr.writeout(msg + "\n\n")out(__doc__)return 0# open a file & return the file object; gripe and return 0 if it # couldn't be opened def fopen(fname):try:return open(fname)except IOError as detail:return fail("couldn't open " + fname + ": " + str(detail))# open two files & spray the diff to stdout; return false iff a problem def fcompare(f1name, f2name):f1 = fopen(f1name)f2 = fopen(f2name)if not f1 or not f2:return 0a = f1.readlines(); f1.close()b = f2.readlines(); f2.close()for line in difflib.ndiff(a, b):print(line, end=' ')return 1# crack args (sys.argv[1:] is normal) & compare; # return false iff a problemdef main(args):import getopttry:opts, args = getopt.getopt(args, "qr:")except getopt.error as detail:return fail(str(detail))noisy = 1qseen = rseen = 0for opt, val in opts:if opt == "-q":qseen = 1noisy = 0elif opt == "-r":rseen = 1whichfile = valif qseen and rseen:return fail("can't specify both -q and -r")if rseen:if args:return fail("no args allowed with -r option")if whichfile in ("1", "2"):restore(whichfile)return 1return fail("-r value must be 1 or 2")if len(args) != 2:return fail("need 2 filename args")f1name, f2name = argsif noisy:print('-:', f1name)print('+:', f2name)return fcompare(f1name, f2name)# read ndiff output from stdin, and print file1 (which=='1') or # file2 (which=='2') to stdoutdef restore(which):restored = difflib.restore(sys.stdin.readlines(), which)sys.stdout.writelines(restored)if __name__ == '__main__':main(sys.argv[1:])