在非常不同的字符串上强制ndiff

时间:2018-03-21 19:07:13

标签: python difflib

来自ndiff的{​​{1}}函数允许一个很好的界面来检测线条的差异。当线条足够接近时它做得很好:

difflib

但是当线条太不相似时,不再可能进行丰富的报告:

>>> print '\n'.join(list(ndiff(['foo*'], ['foot'], )))
- foo*
?    ^

+ foot
?    ^

这是我正在使用的用例,我正在尝试使用>>> print '\n'.join(list(ndiff(['foo'], ['foo*****'], ))) - foo + foo***** (或基础类ndiff)来强制报告,即使字符串太不相似。< / p>

对于失败的例子,我希望得到如下结果:

Differ

2 个答案:

答案 0 :(得分:1)

负责打印上下文的函数(即以?开头的那些行)是Differ._fancy_replace。该函数通过检查两条线是否相等至少75%来工作(参见cutoff变量)。不幸的是,75%的截止值是硬编码的,无法改变。

我可以建议的是继承Differ并提供_fancy_replace的版本,它只是忽略了截止。这是:

from difflib import Differ, SequenceMatcher

class FullContextDiffer(Differ):

    def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
        """
        Copied and adapted from https://github.com/python/cpython/blob/3.6/Lib/difflib.py#L928
        """
        best_ratio = 0
        cruncher = SequenceMatcher(self.charjunk)

        for j in range(blo, bhi):
            bj = b[j]
            cruncher.set_seq2(bj)
            for i in range(alo, ahi):
                ai = a[i]
                if ai == bj:
                    continue
                cruncher.set_seq1(ai)
                if cruncher.real_quick_ratio() > best_ratio and \
                      cruncher.quick_ratio() > best_ratio and \
                      cruncher.ratio() > best_ratio:
                    best_ratio, best_i, best_j = cruncher.ratio(), i, j

        yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)

        aelt, belt = a[best_i], b[best_j]

        atags = btags = ""
        cruncher.set_seqs(aelt, belt)
        for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
            la, lb = ai2 - ai1, bj2 - bj1
            if tag == 'replace':
                atags += '^' * la
                btags += '^' * lb
            elif tag == 'delete':
                atags += '-' * la
            elif tag == 'insert':
                btags += '+' * lb
            elif tag == 'equal':
                atags += ' ' * la
                btags += ' ' * lb
            else:
                raise ValueError('unknown tag %r' % (tag,))
        yield from self._qformat(aelt, belt, atags, btags)

        yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)

这是一个如何运作的例子:

a = [
    'foo',
    'bar',
    'foobar',
]

b = [
    'foo',
    'bar',
    'barfoo',
]

print('\n'.join(FullContextDiffer().compare(a, b)))

# Output:
# 
#   foo
#   bar
# - foobar
# ?    ---
# 
# + barfoo
# ? +++

答案 1 :(得分:0)

这里你想要做的似乎不是跨多行比较,而是跨越字符串。然后你可以直接传递你的字符串,没有列表,你应该得到一个接近你正在寻找的行为。

>>> print ('\n'.join(list(ndiff('foo', 'foo*****'))))
  f
  o
  o
+ *
+ *
+ *
+ *
+ *

即使输出格式不是您要查找的格式,它也会封装正确的信息。我们可以使输出适配器给出正确的格式。

def adapter(out):
    chars = []
    symbols = []

    for c in out:
        chars.append(c[2])
        symbols.append(c[0])

    return ''.join(chars), ''.join(symbols)

这可以这样使用。

>>> print ('\n'.join(adapter(ndiff('foo', 'foo*****'))))
foo*****
   +++++