difflib模块用来辅助计算文本差异,difflib模块提供比较序列的类和函数,可以用来进行序列或文件的比较并产生不同格式的信息,包括HTML和上下文以及统一格式的差异。
difflib文本比较类
difflib提供了用来文本比较的类,有以下内容
SequenceMatcher
SequenceMatcher:序列分析器,可以用于比较任何序列元素是可哈希的序列对,该类比较灵活。所采用的基本算法是在20世纪80年代后期由Ratcliff和Obsershelp提出来的,该基本算法的另一种叫法是”格式塔模式匹配算法”,算法的思想是找到最长的连续匹配子序列,子序列不包含无意义的空行或空字符,之后将该思想应用到递归匹配序列左侧和右侧序列片段。
时间控制&自动标识丢弃垃圾字符串
在时间复杂度上,SequenceMatcher用来做比较时的最坏算法时间复杂度是$O(n^2)$, 最佳情况的时间复杂度是线性的$O(n)$,其预期情况取决于由多少个元素以及元素之间的复杂程度。
垃圾自动标识和触发丢弃功能也是SequenceMatcher的另一项重要功能。SequenceMatcher支持自动将某些序列视为垃圾并自动丢弃。垃圾自动标识是计算每个项目在序列中出现的次数,如果一个项目的重复项占序列的1%以上,并且序列长度至少为200个项目,则该项目会被标记为”受欢迎”被视为垃圾自动丢弃,以便更好的进行序列匹配。在创建SequenceMatcher的时候,可以通过将autojunk
参数设置为false的方式关闭此功能。不过autojunk
参数是Python3.2才有的功能。
SequenceMatcher构建
SequenceMatcher的创建函数用模块级别函数difflib.SequenceMatcher(isjunk=None, a=’’, b=’’, autojunk=True),可选参数isjunk
必须是none
(默认值),或者是一个单参数函数,该函数接受一个序列元素,并且仅当该元素被定义为垃圾字符且应被忽略时才返回true。为isjunk
传递none
等同于传递lambda x:0
;换句话说,不忽略任何元素。如以下lambda函数
lambda x:x in '\t'
以上代表如果将行作为字符序列进行比较,并且在空白或硬制表符上不进行同步比较。
可选参数a
和b
是要比较的序列;都默认为空字符串。两个序列的元素都必须是可哈希的;可选参数autojunk
可用于禁用自动垃圾启发式。
SequenceMatcher对象有三个数据属性:当isjunk
值为true时,bjunk
是b的一组元素;bpoular
是垃圾自动启发式控制,如果bpoular
没有被禁用,则认为出现频率高的字符并非垃圾元素;b2j
是将b
的其余元素映射到它们出现的位置列表的dict
。当通过set_seqs()或set_seq2()重置字符列表b
时,这三个参数都会跟着重置。
SequenceMatcher函数
SequenceMatcher对象有以下操作函数:
set_seqs()
set_seqs(a,b)比较两个字符序列。SequenceMatcher计算并缓存有关第二个序列的详细信息,因此,如果要将一个序列与多个序列进行比较,请使用set_seq2()
将常用的序列设置一次,并对其他每个序列重复调用set_seq1()
。
set_seq1()
set_seq1(a) 设置要比较的第一个序列,要比较的第二个序列不发生改变。
set_seq2()
set_seq2(b)设置要比较的第二个序列,要比较的第一个序列不发生改变。
find_longest_match()
find_longest_match(alo, ahi, blo, bhi)在a[alo:ahi]
和b[blo:bhi]
中查找最长的匹配块,并返回一个被命名的元组Match(a, b, size)。
如果isjunk
被省略或没有,则find longer_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中最早开始的匹配块。
>>> import difflib
>>> s = difflib.SequenceMatcher(None, 'abcd', 'abcd abcd')
>>> s.find_longest_match(0,4,0,9)
Match(a=0, b=0, size=4)
>>>
如果提供了isjunk
,则首先按照上述方法确定最长的匹配块,但有一个附加限制,即块中不显示任何垃圾元素。然后,通过匹配(仅)两侧的垃圾元素,尽可能扩展该块。因此,结果块永远不会在垃圾上匹配,除非相同的垃圾恰好与一个有趣的匹配相邻。
>>> s = difflib.SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd")
>>> s.find_longest_match(0, 5, 0, 9)
Match(a=1, b=0, size=4)
这个举例与上一个举例一样,但是考虑到空白是垃圾。设置isjunk
后将阻止“abcd”直接匹配第二个序列末尾的“abcd”。相反,只有“abcd”可以匹配,并且在第二个序列中与最左边的“abcd”匹配,如果没有匹配块,将返回(alo, blo, 0)
。
get_matching_blocks()
get_matchinig_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 = difflib.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()
get_opcodes()返回描述如何将a
转换为b
的5个元组的列表。每个元组的形式为(tag,i1,i2,j1,j2)
。第一个元组的i1==j1==0
,其余的元组的i1
等于前一个元组的i2
,同样,j1
等于前一个j2
。
tag参数是string形式,其值与含义如下:
值 | 含义 |
---|