1. difflib差異計算工具
此模塊提供用于比較序列的類和函式, 例如,它可以用于比較檔案,并可以產生各種格式的不同資訊,包括HTML和背景關系以及統一格式的差異點,有關目錄和檔案的比較,請參見filecmp模塊,
class difflib.SequenceMatcher(None,s1,s2)
這是一個靈活的類,可用于比較任何型別的序列對,只要序列元素為 hashable 物件, 其基本演算法要早于由 Ratcliff 和 Obershelp 于 1980 年代末期發表并以“格式塔模式匹配”的夸張名稱命名的演算法,并且更加有趣一些, 其思路是找到不包含“垃圾”元素的最長連續匹配子序列;所謂“垃圾”元素是指其在某種意義上沒有價值,例如空白行或空白符, (處理垃圾元素是對 Ratcliff 和 Obershelp 演算法的一個擴展,) 然后同樣的思路將遞回地應用于匹配序列的左右序列片段, 這并不能產生最小編輯序列,但確實能產生在人們看來“正確”的匹配,
1.1 比較文本體
differ類用于處理文本行序列,并生成人類可讀的差異(deltas)或更改指令各行中的差異,differ生成的默認輸出與unix下的diff命令列工具類似,包括表的原始輸入值(包含共同的值),以及指示做了哪些更改的標記資料,
有 - 前綴的行在
第一個序列中,而非第二個序列,有 + 前綴的行在第二個序列中,而非第一個序列,
如果某一行的版本之間存在增量差異,那么會使用一個加 ? 前綴以突出在新版本中的更改,
如果一行未改變,則會列印輸出,而且其左列有一個額外的空格,使它與其有差異的輸出對齊,
將文本傳入compare()之前先將其分解為由單個文本行構成的序列,與傳入串相比,這樣可以生成更可讀的輸出,
import difflib text1 = """Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Integer eu lacus accumsan arcu fermentum euismod. Donec pulvinar porttitor tellus. Aliquam venenatis. Donec facilisis pharetra tortor. In nec mauris eget magna consequat convalis. Nam sed sem vitae odio pellentesque interdum. Sed consequat viverra nisl. Suspendisse arcu metus, blandit quis, rhoncus ac, pharetra eget, velit. Mauris urna. Morbi nonummy molestie orci. Praesent nisi elit, fringilla ac, suscipit non, tristique vel, mauris. Curabitur vel lorem id nisl porta adipiscing. Suspendisse eu lectus. In nunc. Duis vulputate tristique enim. Donec quis lectus a justo imperdiet tempus.""" text1_lines = text1.splitlines() text2 = """Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Integer eu lacus accumsan arcu fermentum euismod. Donec pulvinar, porttitor tellus. Aliquam venenatis. Donec facilisis pharetra tortor. In nec mauris eget magna consequat convalis. Nam cras vitae mi vitae odio pellentesque interdum. Sed consequat viverra nisl. Suspendisse arcu metus, blandit quis, rhoncus ac, pharetra eget, velit. Mauris urna. Morbi nonummy molestie orci. Praesent nisi elit, fringilla ac, suscipit non, tristique vel, mauris. Curabitur vel lorem id nisl porta adipiscing. Duis vulputate tristique enim. Donec quis lectus a justo imperdiet tempus. Suspendisse eu lectus. In nunc.""" text2_lines = text2.splitlines() d = difflib.Differ() diff = d.compare(text1_lines, text2_lines) print('\n'.join(diff))
結果:

示例資料中兩個文本段的開始部分是一樣的,所以第一行會直接列印而沒有任何額外標注,

資料的第三行有變化,修改后的文本中包含有一個逗號,這兩個版本的資料行都會列印,而且第五行上的額外資訊會顯示文本中哪一列有修改,這里顯示增加了 , 字符,

輸出中接下來幾行顯示洗掉了一個多余的空格,

接下來有一個更復雜的改變,其替換了一個短語中的多個單詞,

段落中最后一句變化最大,所以表示差異時完全洗掉了老版本,增加了新版本,
ndiff()函式生成的輸出基本上相同,通過特別“加工”來處理文本資料,并洗掉輸入中的“噪聲”,
differ()類會顯示所有輸入行,統一差異格式(unified diff)則不同,它只包含有修改的文本行和一些背景關系,unified_diff()函式會生成這種輸出,
import difflib text1 = """Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Integer eu lacus accumsan arcu fermentum euismod. Donec pulvinar porttitor tellus. Aliquam venenatis. Donec facilisis pharetra tortor. In nec mauris eget magna consequat convalis. Nam sed sem vitae odio pellentesque interdum. Sed consequat viverra nisl. Suspendisse arcu metus, blandit quis, rhoncus ac, pharetra eget, velit. Mauris urna. Morbi nonummy molestie orci. Praesent nisi elit, fringilla ac, suscipit non, tristique vel, mauris. Curabitur vel lorem id nisl porta adipiscing. Suspendisse eu lectus. In nunc. Duis vulputate tristique enim. Donec quis lectus a justo imperdiet tempus.""" text1_lines = text1.splitlines() text2 = """Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Integer eu lacus accumsan arcu fermentum euismod. Donec pulvinar, porttitor tellus. Aliquam venenatis. Donec facilisis pharetra tortor. In nec mauris eget magna consequat convalis. Nam cras vitae mi vitae odio pellentesque interdum. Sed consequat viverra nisl. Suspendisse arcu metus, blandit quis, rhoncus ac, pharetra eget, velit. Mauris urna. Morbi nonummy molestie orci. Praesent nisi elit, fringilla ac, suscipit non, tristique vel, mauris. Curabitur vel lorem id nisl porta adipiscing. Duis vulputate tristique enim. Donec quis lectus a justo imperdiet tempus. Suspendisse eu lectus. In nunc.""" text2_lines = text2.splitlines() diff = difflib.unified_diff( text1_lines, text2_lines, lineterm='', ) print('\n'.join(diff))
lineterm引數用來告訴unified_diff()不必為它回傳的控制行追加換行符,因為輸入行不包括這些換行符,列印時所有行都會增加換行符,對于很多常用版本控制工具的用戶來說,輸出看上去應該很熟悉,

使用context_diff()會產生類似的可續輸出,
1.2 無用資料
所有生成差異序列的函式都會接受一些引數來指示應當忽略哪些行,以及要忽略一行中的哪些字符,例如,這些引數可用于跳過檔案兩個版本中的標記或空白符改變,
from difflib import SequenceMatcher def show_results(match): print(' a = {}'.format(match.a)) print(' b = {}'.format(match.b)) print(' size = {}'.format(match.size)) i, j, k = match print(' A[a:a+size] = {!r}'.format(A[i:i + k])) print(' B[b:b+size] = {!r}'.format(B[j:j + k])) A = " abcd" B = "abcd abcd" print('A = {!r}'.format(A)) print('B = {!r}'.format(B)) print('\nWithout junk detection:') s1 = SequenceMatcher(None, A, B) match1 = s1.find_longest_match(0, len(A), 0, len(B)) show_results(match1) print('\nTreat spaces as junk:') s2 = SequenceMatcher(lambda x: x == " ", A, B) match2 = s2.find_longest_match(0, len(A), 0, len(B)) show_results(match2)
默認differ不會顯式地忽略任何行或字符,但會依賴SequenceMatcher的能力檢測噪聲,ndiff()的默認行為是忽略空格和制表符,

1.3 比較任意型別
SequenceMatcher類可以比較任意型別的兩個序列,只要它們的值是可散列的,這個類使用一個演算法來標識序列中最長的連續匹配塊,并洗掉對實際資料沒有貢獻的無用值,
函式get_opcodes()回傳一個指令串列來修改第一個序列,使它與第二個序列匹配,這些指令被編碼為5元素元組,包括一個字串指令(“操作碼”)和序列的兩對開始及結束索引(表示為i1、i2、j1和j2),
|
值 |
意義 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
import difflib s1 = [1, 2, 3, 5, 6, 4] s2 = [2, 3, 5, 4, 6, 1] print('Initial data:') print('s1 =', s1) print('s2 =', s2) print('s1 == s2:', s1 == s2) print() matcher = difflib.SequenceMatcher(None, s1, s2) for tag, i1, i2, j1, j2 in reversed(matcher.get_opcodes()): if tag == 'delete': print('Remove {} from positions [{}:{}]'.format( s1[i1:i2], i1, i2)) print(' before =', s1) del s1[i1:i2] elif tag == 'equal': print('s1[{}:{}] and s2[{}:{}] are the same'.format( i1, i2, j1, j2)) elif tag == 'insert': print('Insert {} from s2[{}:{}] into s1 at {}'.format( s2[j1:j2], j1, j2, i1)) print(' before =', s1) s1[i1:i2] = s2[j1:j2] elif tag == 'replace': print(('Replace {} from s1[{}:{}] ' 'with {} from s2[{}:{}]').format( s1[i1:i2], i1, i2, s2[j1:j2], j1, j2)) print(' before =', s1) s1[i1:i2] = s2[j1:j2] print(' after =', s1, '\n') print('s1 == s2:', s1 == s2)
這個例子比較了兩個整數串列,并使用get_opcodes()得出將原串列轉換為新串列的指令,這里以逆序應用所做的修改,以便增加和洗掉元素之后串列索引仍是正確的,

SequenceMatcher用于處理定制類以及內置型別,前提是它們必須是可散列的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/193112.html
標籤:Python
上一篇:QOpenGLWidget中如何實作updateGL的功能?
下一篇:關于malloc與free的問題
