python3从零学习-5.1.4、对比模块Difflib

        此模块提供用于比较序列的类和函数。 例如,它可以用于比较文件,并可以产生各种格式的不同信息,包括 HTML 和上下文以及统一格式的差异点。 有关目录和文件的比较,请参见 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” 超链接将下一个突出显示差异部分放在浏览器顶端,不添加任何前导上下文)。

在 3.5 版更改: 增加了 charset 关键字参数。 HTML 文档的默认字符集从 'ISO-8859-1' 更改为 'utf-8'。

make_table(fromlines, tolines, fromdesc='', todesc='', context=False, numlines=5)

比较 fromlines 和 tolines (字符串列表) 并返回一个字符串,表示一个包含各行差异的完整 HTML 表格,行间与行外的更改将突出显示。

此方法的参数与 make_file() 方法的相同。

Tools/scripts/diff.py 是这个类的命令行前端,其中包含一个很好的使用示例。

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 格式表示。 如果未指定,这些字符串默认为空。

>>>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 为一个指定目标近似匹配的序列(通常为字符串),possibilities 为一个由用于匹配 word 的序列构成的列表(通常为字符串列表)。

可选参数 n (默认为 3) 指定最多返回多少个近似匹配; n 必须大于 0.

可选参数 cutoff (默认为 0.6) 是一个 [0, 1] 范围内的浮点数。 与 word 相似度得分未达到该值的候选匹配将被忽略。

候选匹配中(不超过 n 个)的最佳匹配将以列表形式返回,按相似度得分排序,最相似的排在最前面。

>>>get_close_matches('appel', ['ape','apple','peach','puppy'])

['apple', 'ape']

>>>importkeyword

>>>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(),它会过滤掉空白字符(空格符或制表符;但包含换行符可不是个好主意!)。

Tools/scripts/ndiff.py 是这个函数的命令行前端。

>>>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

+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 (字节串对象列表);产生以 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)

Return true for ignorable lines. The line line is ignorable if line is blank or contains a single '#', otherwise it is not ignorable. Used as a default for parameter linejunk in ndiff() in older versions.

difflib.IS_CHARACTER_JUNK(ch)

Return true for ignorable characters. The character ch is ignorable if ch is a space or tab, otherwise it is not ignorable. Used as a default for parameter charjunk in ndiff().

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343