在Python中生成和应用差异

35 投票
6 回答
26237 浏览
提问于 2025-04-15 19:33

在Python中,有没有一种现成的方法可以生成两个文本之间的差异列表,然后再把这个差异应用到一个文件上,以便得到另一个文件呢?

我想保留文本的修订历史,但如果只是修改了一行,我不想为每次修订都保存整个文本。我查看了difflib,但是我没有找到如何生成仅包含编辑行的列表,这样可以用来修改一个文本以得到另一个文本。

6 个回答

5

我写了一个纯Python的函数,可以用来应用差异补丁,从而恢复输入的字符串,希望对大家有帮助。这个函数使用了统一差异格式来解析数据。

import re

_hdr_pat = re.compile("^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@$")

def apply_patch(s,patch,revert=False):
  """
  Apply unified diff patch to string s to recover newer string.
  If revert is True, treat s as the newer string, recover older string.
  """
  s = s.splitlines(True)
  p = patch.splitlines(True)
  t = ''
  i = sl = 0
  (midx,sign) = (1,'+') if not revert else (3,'-')
  while i < len(p) and p[i].startswith(("---","+++")): i += 1 # skip header lines
  while i < len(p):
    m = _hdr_pat.match(p[i])
    if not m: raise Exception("Cannot process diff")
    i += 1
    l = int(m.group(midx))-1 + (m.group(midx+1) == '0')
    t += ''.join(s[sl:l])
    sl = l
    while i < len(p) and p[i][0] != '@':
      if i+1 < len(p) and p[i+1][0] == '\\': line = p[i][:-1]; i += 2
      else: line = p[i]; i += 1
      if len(line) > 0:
        if line[0] == sign or line[0] == ' ': t += line[1:]
        sl += (line[0] != sign)
  t += ''.join(s[sl:])
  return t

如果有头部信息行 ("--- ...\n","+++ ...\n"),它会跳过这些行。如果我们有一个统一差异字符串 diffstr,它表示 oldstrnewstr 之间的差异:

# recreate `newstr` from `oldstr`+patch
newstr = apply_patch(oldstr, diffstr)
# recreate `oldstr` from `newstr`+patch
oldstr = apply_patch(newstr, diffstr, True)

在Python中,你可以使用 difflib(这是标准库的一部分)来生成两个字符串的统一差异:

import difflib
_no_eol = "\ No newline at end of file"

def make_patch(a,b):
  """
  Get unified string diff between two strings. Trims top two lines.
  Returns empty string if strings are identical.
  """
  diffs = difflib.unified_diff(a.splitlines(True),b.splitlines(True),n=0)
  try: _,_ = next(diffs),next(diffs)
  except StopIteration: pass
  return ''.join([d if d[-1] == '\n' else d+'\n'+_no_eol+'\n' for d in diffs])

在Unix系统上,你可以用 diff -U0 a.txt b.txt 来比较两个文件。

代码可以在GitHub上找到,里面还有使用ASCII和随机Unicode字符的测试:https://gist.github.com/noporpoise/16e731849eb1231e86d78f9dfeca3abc

12

你知道difflib.unified_diff这个工具吗?它可能正好符合你的需求。这里有一个例子,可以看看

不过,原来的链接坏掉了。你可以在这个新的链接找到一个例子:这里

31

你有没有看过谷歌的 diff-match-patch 呢?听说谷歌文档就是用这一套算法的。它不仅有比较(diff)模块,还有修补(patch)模块,这样你就可以通过旧文件和比较结果生成最新的文件。

里面还包含了一个 Python 版本。

http://code.google.com/p/google-diff-match-patch/

撰写回答