如何使用difflib.ndiff忽略某些行?

2 投票
2 回答
5572 浏览
提问于 2025-04-15 17:48

根据文档,你可以提供一个叫做linejunk的函数来忽略某些行。不过,我试了试,发现它不起作用。下面是一些示例代码,供大家讨论:

from re import search
from difflib import ndiff 
t1 = 'one 1\ntwo 2\nthree 3'
t2 = 'one 1\ntwo 29\nthree 3'
diff = ndiff(t1.splitlines(), t2.splitlines(), lambda x: search('2', x))

我的目的是忽略第二行,这样diff就会是一个不显示任何差异的生成器。

谢谢大家的帮助。

2 个回答

8

我最近也遇到了同样的问题。

这是我发现的一些内容:

参考一下 http://bugs.python.org/issue14332

这些*垃圾参数的主要目的是加快匹配速度,以便找到差异,而不是掩盖差异

再看看 http://hg.python.org/cpython/rev/0a69b1e8b7fe/

这个补丁对difflib文档中“垃圾”和“忽略”这两个概念提供了更好的解释。

这些垃圾过滤函数加快了匹配速度以找到差异,并且不会导致任何不同的行或字符被忽略。

1

你的例子有个问题:ndiff的前两个参数应该是字符串的列表;而你给的是一个单独的字符串,这个字符串会被当作字符列表来处理。你可以查看文档了解更多。比如可以用 t1 = 'one 1\ntwo 2\nthree 3'.splitlines() 来处理。

不过,正如下面的例子所示,difflib.ndiff并不会对所有行都调用linejunk函数。这种行为已经存在很久了——从Python 2.2到2.6,以及3.1都验证过。

示例脚本:

from difflib import ndiff
t1 = 'one 1\ntwo 2\nthree 3'.splitlines()
t2 = 'one 1\ntwo 29\nthree 3'.splitlines()
def lj(line):
    rval = '2' in line
    print("lj: line=%r, rval=%s" % (line, rval))
    return rval
d = list(ndiff(t1, t2    )); print("%d %r\n" %  (1, d))
d = list(ndiff(t1, t2, lj)); print("%d %r\n" %  (2, d))
d = list(ndiff(t2, t1, lj)); print("%d %r\n" %  (3, d))

在Python 2.6中运行的输出:

1 ['  one 1', '- two 2', '+ two 29', '?      +\n', '  three 3']

lj: line='one 1', rval=False
lj: line='two 29', rval=True
lj: line='three 3', rval=False
2 ['  one 1', '- two 2', '+ two 29', '?      +\n', '  three 3']

lj: line='one 1', rval=False
lj: line='two 2', rval=True
lj: line='three 3', rval=False
3 ['  one 1', '- two 29', '?      -\n', '+ two 2', '  three 3']

你可能想把这个报告为一个bug。不过要注意,文档中并没有明确说明“垃圾”行的具体含义。你期待的输出是什么呢?

还有一个让人困惑的地方:在脚本中添加这些行:

t3 = 'one 1\n   \ntwo 2\n'.splitlines()
t4 = 'one 1\n\n#\n\ntwo 2\n'.splitlines()
d = list(ndiff(t3, t4      )); print("%d %r\n" %  (4, d))
d = list(ndiff(t4, t3      )); print("%d %r\n" %  (5, d))
d = list(ndiff(t3, t4, None)); print("%d %r\n" %  (6, d))
d = list(ndiff(t4, t3, None)); print("%d %r\n" %  (7, d))

会产生这样的输出:

4 ['  one 1', '-    ', '+ ', '+ #', '+ ', '  two 2']

5 ['  one 1', '+    ', '- ', '- #', '- ', '  two 2']

6 ['  one 1', '-    ', '+ ', '+ #', '+ ', '  two 2']

7 ['  one 1', '+    ', '- ', '- #', '- ', '  two 2']

换句话说,使用默认的linejunk函数的结果和不使用linejunk函数的结果是一样的,尤其是在包含不同“垃圾”行的情况下(除了开头的井号外都是空白)。

也许如果你能告诉我们你想要实现什么,我们可能会建议一种替代的方法。

在获取更多信息后的编辑

如果你的意图是一般性地忽略所有包含'2'的行,也就是说在ndiff的处理上假装这些行不存在,你只需要把这种假装变成现实:

t1f = [line for line in t1 if '2' not in line]
t2f = [line for line in t2 if '2' not in line]
diff = ndiff(t1f, t2f)

撰写回答