difflib
--- 計算差異的輔助工具?
源代碼: Lib/difflib.py
此模塊提供用于比較序列的類(lèi)和函數。 例如,它可被用于比較文件,并可產(chǎn)生多種格式的不同文件差異信息,包括 HTML 和上下文以及統一的 diff 數據。 有關(guān)比較目錄和文件,另請參閱 filecmp
模塊。
- class difflib.SequenceMatcher
這是一個(gè)靈活的類(lèi),可用于比較任何類(lèi)型的序列對,只要序列元素為 hashable 對象。 其基本算法要早于由 Ratcliff 和 Obershelp 于 1980 年代末期發(fā)表并以“格式塔模式匹配”的夸張名稱(chēng)命名的算法,并且更加有趣一些。 其思路是找到不包含“垃圾”元素的最長(cháng)連續匹配子序列;所謂“垃圾”元素是指其在某種意義上沒(méi)有價(jià)值,例如空白行或空白符。 (處理垃圾元素是對 Ratcliff 和 Obershelp 算法的一個(gè)擴展。) 然后同樣的思路將遞歸地應用于匹配序列的左右序列片段。 這并不能產(chǎn)生最小編輯序列,但確實(shí)能產(chǎn)生在人們看來(lái)“正確”的匹配。
耗時(shí): 基本 Ratcliff-Obershelp 算法在最壞情況下為立方時(shí)間而在一般情況下為平方時(shí)間。
SequenceMatcher
在最壞情況下為平方時(shí)間而在一般情況下的行為受到序列中有多少相同元素這一因素的微妙影響;在最佳情況下則為線(xiàn)性時(shí)間。自動(dòng)垃圾啟發(fā)式計算:
SequenceMatcher
支持使用啟發(fā)式計算來(lái)自動(dòng)將特定序列項視為垃圾。 這種啟發(fā)式計算會(huì )統計每個(gè)單獨項在序列中出現的次數。 如果某一項(在第一項之后)的重復次數超過(guò)序列長(cháng)度的 1% 并且序列長(cháng)度至少有 200 項,該項會(huì )被標記為“熱門(mén)”并被視為序列匹配中的垃圾。 這種啟發(fā)式計算可以通過(guò)在創(chuàng )建SequenceMatcher
時(shí)將autojunk
參數設為False
來(lái)關(guān)閉。3.2 新版功能: autojunk 形參。
- class difflib.Differ?
這個(gè)類(lèi)的作用是比較由文本行組成的序列,并產(chǎn)生可供人閱讀的差異或增量信息。 Differ 統一使用
SequenceMatcher
來(lái)完成行序列的比較以及相似(接近匹配)行內部字符序列的比較。Differ
增量的每一行均以雙字母代碼打頭:雙字母代碼
含意
'- '
行為序列 1 所獨有
'+ '
行為序列 2 所獨有
' '
行在兩序列中相同
'? '
行不存在于任一輸入序列
Lines beginning with '
?
' attempt to guide the eye to intraline differences, and were not present in either input sequence. These lines can be confusing if the sequences contain whitespace characters, such as spaces, tabs or line breaks.
- class difflib.HtmlDiff?
這個(gè)類(lèi)可用于創(chuàng )建 HTML 表格(或包含表格的完整 HTML 文件)以并排地逐行顯示文本比較,行間與行外的更改將突出顯示。 此表格可以基于完全或上下文差異模式來(lái)生成。
這個(gè)類(lèi)的構造函數:
- __init__(tabsize=8, wrapcolumn=None, linejunk=None, charjunk=IS_CHARACTER_JUNK)?
初始化
HtmlDiff
的實(shí)例。tabsize 是一個(gè)可選關(guān)鍵字參數,指定制表位的間隔,默認值為
8
。wrapcolumn 是一個(gè)可選關(guān)鍵字參數,指定行文本自動(dòng)打斷并換行的列位置,默認值為
None
表示不自動(dòng)換行。linejunk 和 charjunk 均是可選關(guān)鍵字參數,會(huì )傳入
ndiff()
(被HtmlDiff
用來(lái)生成并排顯示的 HTML 差異)。 請參閱ndiff()
文檔了解參數默認值及其說(shuō)明。
下列是公開(kāi)的方法
- make_file(fromlines, tolines, fromdesc='', todesc='', context=False, numlines=5, *, charset='utf-8')?
比較 fromlines 和 tolines (字符串列表) 并返回一個(gè)字符串,表示一個(gè)完整 HTML 文件,其中包含各行差異的表格,行間與行外的更改將突出顯示。
fromdesc 和 todesc 均是可選關(guān)鍵字參數,指定來(lái)源/目標文件的列標題字符串(默認均為空白字符串)。
context 和 numlines 均是可選關(guān)鍵字參數。 當只要顯示上下文差異時(shí)就將 context 設為
True
,否則默認值False
為顯示完整文件。 numlines 默認為5
。 當 context 為True
時(shí) numlines 將控制圍繞突出顯示差異部分的上下文行數。 當 context 為False
時(shí) numlines 將控制在使用 "next" 超鏈接時(shí)突出顯示差異部分之前所顯示的行數(設為零則會(huì )導致 "next" 超鏈接將下一個(gè)突出顯示差異部分放在瀏覽器頂端,不添加任何前導上下文)。備注
fromdesc 和 todesc 會(huì )被當作未轉義的 HTML 來(lái)解讀,當接收不可信來(lái)源的輸入時(shí)應該適當地進(jìn)行轉義。
在 3.5 版更改: 增加了 charset 關(guān)鍵字參數。 HTML 文檔的默認字符集從
'ISO-8859-1'
更改為'utf-8'
。
- make_table(fromlines, tolines, fromdesc='', todesc='', context=False, numlines=5)?
比較 fromlines 和 tolines (字符串列表) 并返回一個(gè)字符串,表示一個(gè)包含各行差異的完整 HTML 表格,行間與行外的更改將突出顯示。
此方法的參數與
make_file()
方法的相同。
Tools/scripts/diff.py
是這個(gè)類(lèi)的命令行前端,其中包含一個(gè)很好的使用示例。
- difflib.context_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n')?
比較 a 和 b (字符串列表);返回上下文差異格式的增量信息 (一個(gè)產(chǎn)生增量行的 generator)。
所謂上下文差異是一種只顯示有更改的行再加幾個(gè)上下文行的緊湊形式。 更改被顯示為之前/之后的樣式。 上下文行數由 n 設定,默認為三行。
默認情況下,差異控制行(以
***
or---
表示)是通過(guò)末尾換行符來(lái)創(chuàng )建的。 這樣做的好處是從io.IOBase.readlines()
創(chuàng )建的輸入將得到適用于io.IOBase.writelines()
的差異信息,因為輸入和輸出都帶有末尾換行符。對于沒(méi)有末尾換行符的輸入,應將 lineterm 參數設為
""
,這樣輸出內容將統一不帶換行符。上下文差異格式通常帶有一個(gè)記錄文件名和修改時(shí)間的標頭。 這些信息的部分或全部可以使用字符串 fromfile, tofile, fromfiledate 和 tofiledate 來(lái)指定。 修改時(shí)間通常以 ISO 8601 格式表示。 如果未指定,這些字符串默認為空。
>>> 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 ! ham guido --- 1,4 ---- ! python ! eggy ! hamster guido
請參閱 difflib 的命令行接口 獲取更詳細的示例。
- difflib.get_close_matches(word, possibilities, n=3, cutoff=0.6)?
返回由最佳“近似”匹配構成的列表。 word 為一個(gè)指定目標近似匹配的序列(通常為字符串),possibilities 為一個(gè)由用于匹配 word 的序列構成的列表(通常為字符串列表)。
可選參數 n (默認為
3
) 指定最多返回多少個(gè)近似匹配; n 必須大于0
.可選參數 cutoff (默認為
0.6
) 是一個(gè) [0, 1] 范圍內的浮點(diǎn)數。 與 word 相似度得分未達到該值的候選匹配將被忽略。候選匹配中(不超過(guò) n 個(gè))的最佳匹配將以列表形式返回,按相似度得分排序,最相似的排在最前面。
>>> 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
形式的增量信息 (一個(gè)產(chǎn)生增量行的 generator)。可選關(guān)鍵字形參 linejunk 和 charjunk 均為過(guò)濾函數 (或為
None
):linejunk: 此函數接受單個(gè)字符串參數,如果其為垃圾字符串則返回真值,否則返回假值。 默認為
None
。 此外還有一個(gè)模塊層級的函數IS_LINE_JUNK()
,它會(huì )過(guò)濾掉沒(méi)有可見(jiàn)字符的行,除非該行添加了至多一個(gè)井號符 ('#'
) -- 但是下層的SequenceMatcher
類(lèi)會(huì )動(dòng)態(tài)分析哪些行的重復頻繁到足以形成噪音,這通常會(huì )比使用此函數的效果更好。charjunk: 此函數接受一個(gè)字符(長(cháng)度為 1 的字符串),如果其為垃圾字符則返回真值,否則返回假值。 默認為模塊層級的函數
IS_CHARACTER_JUNK()
,它會(huì )過(guò)濾掉空白字符(空格符或制表符;但包含換行符可不是個(gè)好主意?。?。Tools/scripts/ndiff.py
是這個(gè)函數的命令行前端。>>> 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)?
返回兩個(gè)序列中產(chǎn)生增量的那一個(gè)。
給出一個(gè)由
Differ.compare()
或ndiff()
產(chǎn)生的 序列,提取出來(lái)自文件 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 (字符串列表);返回統一差異格式的增量信息 (一個(gè)產(chǎn)生增量行的 generator)。
所以統一差異是一種只顯示有更改的行再加幾個(gè)上下文行的緊湊形式。 更改被顯示為內聯(lián)的樣式(而不是分開(kāi)的之前/之后文本塊)。 上下文行數由 n 設定,默認為三行。
默認情況下,差異控制行 (以
---
,+++
或@@
表示) 是通過(guò)末尾換行符來(lái)創(chuàng )建的。 這樣做的好處是從io.IOBase.readlines()
創(chuàng )建的輸入將得到適用于io.IOBase.writelines()
的差異信息,因為輸入和輸出都帶有末尾換行符。對于沒(méi)有末尾換行符的輸入,應將 lineterm 參數設為
""
,這樣輸出內容將統一不帶換行符。上下文差異格式通常帶有一個(gè)記錄文件名和修改時(shí)間的標頭。 這些信息的部分或全部可以使用字符串 fromfile, tofile, fromfiledate 和 tofiledate 來(lái)指定。 修改時(shí)間通常以 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 +hamster guido
請參閱 difflib 的命令行接口 獲取更詳細的示例。
- difflib.diff_bytes(dfunc, a, b, fromfile=b'', tofile=b'', fromfiledate=b'', tofiledate=b'', n=3, lineterm=b'\n')?
使用 dfunc 比較 a 和 b (字節串對象列表);產(chǎn)生以 dfunc 所返回格式表示的差異行列表(也是字節串)。 dfunc 必須是可調用對象,通常為
unified_diff()
或context_diff()
。允許你比較編碼未知或不一致的數據。 除 n 之外的所有輸入都必須為字節串對象而非字符串。 作用方式為無(wú)損地將所有輸入 (除 n 之外) 轉換為字符串,并調用
dfunc(a, b, fromfile, tofile, fromfiledate, tofiledate, n, lineterm)
。 dfunc 的輸出會(huì )被隨即轉換回字節串,這樣你所得到的增量行將具有與 a 和 b 相同的未知/不一致編碼。3.5 新版功能.
- difflib.IS_LINE_JUNK(line)?
對于可忽略的行返回
True
。 如果 line 為空行或只包含單個(gè)'#'
則 line 行就是可忽略的,否則就是不可忽略的。 此函數被用作較舊版本ndiff()
中 linejunk 形參的默認值。
- difflib.IS_CHARACTER_JUNK(ch)?
對于可忽略的字符返回
True
。 字符 ch 如果為空格符或制表符則 ch 就是可忽略的,否則就是不可忽略的。 此函數被用作ndiff()
中 charjunk 形參的默認值。
參見(jiàn)
- 模式匹配:格式塔方法
John W. Ratcliff 和 D. E. Metzener 對于一種類(lèi)似算法的討論。 此文于 1988 年 7 月發(fā)表于 Dr. Dobb's Journal。
SequenceMatcher 對象?
SequenceMatcher
類(lèi)具有這樣的構造器:
- class difflib.SequenceMatcher(isjunk=None, a='', b='', autojunk=True)?
可選參數 isjunk 必須為
None
(默認值) 或為接受一個(gè)序列元素并當且僅當其為應忽略的“垃圾”元素時(shí)返回真值的單參數函數。 傳入None
作為 isjunk 的值就相當于傳入lambda x: False
;也就是說(shuō)不忽略任何值。 例如,傳入:lambda x: x in " \t"
如果你以字符序列的形式對行進(jìn)行比較,并且不希望區分空格符或硬制表符。
可選參數 a 和 b 為要比較的序列;兩者默認為空字符串。 兩個(gè)序列的元素都必須為 hashable。
可選參數 autojunk 可用于啟用自動(dòng)垃圾啟發(fā)式計算。
3.2 新版功能: autojunk 形參。
SequenceMatcher 對象接受三個(gè)數據屬性: bjunk 是 b 當中 isjunk 為
True
的元素集合;bpopular 是被啟發(fā)式計算(如果其未被禁用)視為熱門(mén)候選的非垃圾元素集合;b2j 是將 b 當中剩余元素映射到一個(gè)它們出現位置列表的字典。 所有三個(gè)數據屬性將在 b 通過(guò)set_seqs()
或set_seq2()
重置時(shí)被重置。3.2 新版功能: bjunk 和 bpopular 屬性。
SequenceMatcher
對象具有以下方法:- set_seqs(a, b)?
設置要比較的兩個(gè)序列。
SequenceMatcher
計算并緩存有關(guān)第二個(gè)序列的詳細信息,這樣如果你想要將一個(gè)序列與多個(gè)序列進(jìn)行比較,可使用set_seq2()
一次性地設置該常用序列并重復地對每個(gè)其他序列各調用一次set_seq1()
。- set_seq1(a)?
設置要比較的第一個(gè)序列。 要比較的第二個(gè)序列不會(huì )改變。
- set_seq2(b)?
設置要比較的第二個(gè)序列。 要比較的第一個(gè)序列不會(huì )改變。
- find_longest_match(alo=0, ahi=None, blo=0, bhi=None)?
找出
a[alo:ahi]
和b[blo:bhi]
中的最長(cháng)匹配塊。如果 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
。 對于所有滿(mǎn)足這些條件的(i', j', k')
,如果i == i'
,j <= j'
也被滿(mǎn)足,則附加條件k >= k'
,i <= i'
。 換句話(huà)說(shuō),對于所有最長(cháng)匹配塊,返回在 a 當中最先出現的一個(gè),而對于在 a 當中最先出現的所有最長(cháng)匹配塊,則返回在 b 當中最先出現的一個(gè)。>>> s = SequenceMatcher(None, " abcd", "abcd abcd") >>> s.find_longest_match(0, 5, 0, 9) Match(a=0, b=4, size=5)
如果提供了 isjunk,將按上述規則確定第一個(gè)最長(cháng)匹配塊,但額外附加不允許塊內出現垃圾元素的限制。 然后將通過(guò)(僅)匹配兩邊的垃圾元素來(lái)盡可能地擴展該塊。 這樣結果塊絕對不會(huì )匹配垃圾元素,除非同樣的垃圾元素正好與有意義的匹配相鄰。
這是與之前相同的例子,但是將空格符視為垃圾。 這將防止
' abcd'
直接與第二個(gè)序列末尾的' abcd'
相匹配。 而只可以匹配'abcd'
,并且是匹配第二個(gè)序列最左邊的'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)
。此方法將返回一個(gè) named tuple
Match(a, b, size)
。在 3.9 版更改: 加入默認參數。
- get_matching_blocks()?
返回描述非重疊匹配子序列的三元組列表。 每個(gè)三元組的形式為
(i, j, n)
,其含義為a[i:i+n] == b[j:j+n]
。 這些三元組按 i 和 j 單調遞增排列。最后一個(gè)三元組用于占位,其值為
(len(a), len(b), 0)
。 它是唯一n == 0
的三元組。 如果(i, j, n)
和(i', j', n')
是在列表中相鄰的三元組,且后者不是列表中的最后一個(gè)三元組,則i+n < i'
或j+n < j'
;換句話(huà)說(shuō),相鄰的三元組總是描述非相鄰的相等塊。>>> 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 變?yōu)?b 的 5 元組列表,每個(gè)元組的形式為
(tag, i1, i2, j1, j2)
。 在第一個(gè)元組中i1 == j1 == 0
,而在其余的元組中 i1 等于前一個(gè)元組的 i2,并且 j1 也等于前一個(gè)元組的 j2。tag 值為字符串,其含義如下:
值
含意
'replace'
a[i1:i2]
應由b[j1:j2]
替換。'delete'
a[i1:i2]
應被刪除。 請注意在此情況下j1 == j2
。'insert'
b[j1:j2]
應插入到a[i1:i1]
。 請注意在此情況下i1 == i2
。'equal'
a[i1:i2] == b[j1:j2]
(兩個(gè)子序列相同)。例如:
>>> 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)?
返回一個(gè)帶有最多 n 行上下文的分組的 generator。
從
get_opcodes()
所返回的組開(kāi)始,此方法會(huì )拆分出較小的更改簇并消除沒(méi)有更改的間隔區域。這些分組以與
get_opcodes()
相同的格式返回。
- ratio()?
返回一個(gè)取值范圍 [0, 1] 的浮點(diǎn)數作為序列相似性度量。
其中 T 是兩個(gè)序列中元素的總數量,M 是匹配的數量,即 2.0*M / T。 請注意如果兩個(gè)序列完全相同則該值為
1.0
,如果兩者完全不同則為0.0
。如果
get_matching_blocks()
或get_opcodes()
尚未被調用則此方法運算消耗較大,在此情況下你可能需要先調用quick_ratio()
或real_quick_ratio()
來(lái)獲取一個(gè)上界。備注
注意:
ratio()
調用的結果可能會(huì )取決于參數的順序。 例如:>>> SequenceMatcher(None, 'tide', 'diet').ratio() 0.25 >>> SequenceMatcher(None, 'diet', 'tide').ratio() 0.5
這三個(gè)返回匹配部分占字符總數的比率的方法可能由于不同的近似級別而給出不一樣的結果,但是 quick_ratio()
和 real_quick_ratio()
總是會(huì )至少與 ratio()
一樣大:
>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75
>>> s.quick_ratio()
0.75
>>> s.real_quick_ratio()
1.0
SequenceMatcher 的示例?
以下示例比較兩個(gè)字符串,并將空格視為“垃圾”:
>>> s = SequenceMatcher(lambda x: x == " ",
... "private Thread currentThread;",
... "private volatile Thread currentThread;")
ratio()
返回一個(gè) [0, 1] 范圍內的整數作為兩個(gè)序列相似性的度量。 根據經(jīng)驗,ratio()
值超過(guò) 0.6 就意味著(zhù)兩個(gè)序列是近似匹配的:
>>> print(round(s.ratio(), 3))
0.866
如果你只對兩個(gè)序列相匹配的位置感興趣,則 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()
返回的最后一個(gè)元組總是只用于占位的 (len(a), len(b), 0)
,這也是元組末尾元素(匹配的元素數量)為 0
的唯一情況。
如果你想要知道如何將第一個(gè)序列轉成第二個(gè)序列,可以使用 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]
參見(jiàn)
此模塊中的
get_close_matches()
函數顯示了如何基于SequenceMatcher
構建簡(jiǎn)單的代碼來(lái)執行有用的功能。使用
SequenceMatcher
構建小型應用的 簡(jiǎn)易版本控制方案。
Differ 對象?
請注意 Differ
所生成的增量并不保證是 最小 差異。 相反,最小差異往往是違反直覺(jué)的,因為它們會(huì )同步任何可能的地方,有時(shí)甚至意外產(chǎn)生相距 100 頁(yè)的匹配。 將同步點(diǎn)限制為連續匹配保留了一些局部性概念,這偶爾會(huì )帶來(lái)產(chǎn)生更長(cháng)差異的代價(jià)。
Differ
類(lèi)具有這樣的構造器:
- class difflib.Differ(linejunk=None, charjunk=None)
可選關(guān)鍵字形參 linejunk 和 charjunk 均為過(guò)濾函數 (或為
None
):linejunk: 接受單個(gè)字符串作為參數的函數,如果其為垃圾字符串則返回真值。 默認值為
None
,意味著(zhù)沒(méi)有任何行會(huì )被視為垃圾行。charjunk: 接受單個(gè)字符(長(cháng)度為 1 的字符串)作為參數的函數,如果其為垃圾字符則返回真值。 默認值為
None
,意味著(zhù)沒(méi)有任何字符會(huì )被視為垃圾字符。這些垃圾過(guò)濾函數可加快查找差異的匹配速度,并且不會(huì )導致任何差異行或字符被忽略。 請閱讀
find_longest_match()
方法的 isjunk 形參的描述了解詳情。Differ
對象是通過(guò)一個(gè)單獨方法來(lái)使用(生成增量)的:- compare(a, b)?
比較兩個(gè)由行組成的序列,并生成增量(一個(gè)由行組成的序列)。
每個(gè)序列必須包含一個(gè)以換行符結尾的單行字符串。 這樣的序列可以通過(guò)文件類(lèi)對象的
readlines()
方法來(lái)獲取。 所生成的增量同樣由以換行符結尾的字符串構成,可以通過(guò)文件類(lèi)對象的writelines()
方法原樣打印出來(lái)。
Differ 示例?
此示例比較兩段文本。 首先我們設置文本為以換行符結尾的單行字符串構成的序列(這樣的序列也可以通過(guò)文件類(lèi)對象的 readlines()
方法來(lái)獲?。?/p>
>>> 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)
接下來(lái)我們實(shí)例化一個(gè) Differ 對象:
>>> d = Differ()
請注意在實(shí)例化 Differ
對象時(shí)我們可以傳入函數來(lái)過(guò)濾掉“垃圾”行和字符。 詳情參見(jiàn) Differ()
構造器說(shuō)明。
最后,我們比較兩個(gè)序列:
>>> result = list(d.compare(text1, text2))
result
是一個(gè)字符串列表,讓我們將其美化打印出來(lái):
>>> 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']
作為單獨的多行字符串顯示出來(lái)則是這樣:
>>> 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 的命令行接口?
這個(gè)實(shí)例演示了如何使用 difflib 來(lái)創(chuàng )建一個(gè)類(lèi)似于 diff
的工具。 它同樣包含在 Python 源碼發(fā)布包中,文件名為 Tools/scripts/diff.py
。
#!/usr/bin/env python3
""" 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, timezone
def 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.lines
fromfile = options.fromfile
tofile = options.tofile
fromdate = 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()