使用emacs/python/winmerge在两个差异文件中映射行号

4 投票
2 回答
579 浏览
提问于 2025-04-17 07:26

考虑下面这两个稍有不同的文件:

foo(旧版本):

<Line 1> a
<Line 2> b
<Line 3> c
<Line 4> d

foo(新版本):

<Line 1> a
<Line 2> e
<Line 3> b
<Line 4> c
<Line 5> f
<Line 6> d

你可以看到,在新文件中出现了字符 ef

我有一组旧文件中的行号,比如 134(对应的字母是 acd)。

有没有办法在这两个文件之间进行映射,这样我就能找到新文件中对应字符的行号呢?

比如,结果会是:

Old file line numbers (1,3,4) ===> New File line numbers (1,4,6)

不幸的是,我手头只有 emacs(带有可用的 ediff)、Python 和 winmerge。

2 个回答

3

你可以在Emacs这个编辑器里完成所有操作:

(defun get-joint-index (file-a index file-b)
  (let ((table (make-hash-table :test #'equal)))
    (flet ((line () (buffer-substring-no-properties
                     (point-at-bol) (point-at-eol))))
      (with-temp-buffer (insert-file file-b)
        (loop for i from 1 do (puthash (line) i table)
              while (zerop (forward-line))))
      (with-temp-buffer (insert-file file-a)
        (loop for i in index do (goto-line i)
              collect (gethash (line) table))))))

要运行这个代码,

M-:(get-joint-index "/tmp/old" '(1 3 4) "/tmp/new")

结果会是 (1 4 6)

2

你需要的是一种字符串搜索算法,这种算法可以让你在一段文本中查找多个模式(也就是旧版本foo中的那些行)。Rabin-Karp算法就是一种适合这种任务的算法。我已经把它调整为适合你的问题:

def linematcher(haystack, needles, lineNumbers):
    f = open(needles)
    needles = [line.strip() for n, line in enumerate(f, 1) if n in lineNumbers]
    f.close()

    hsubs = set(hash(s) for s in needles)
    for n, lineWithNewline in enumerate(open(haystack), 1):
        line = lineWithNewline.strip()
        hs = hash(line)
        if hs in hsubs and line in needles:
            print "{0} ===> {1}".format(lineNumbers[needles.index(line)], n)

假设你的两个文件分别叫做old_foo.txtnew_foo.txt,那么你可以这样调用这个函数:

linematcher('new_foo.txt', 'old_foo.txt', [1, 3, 4])

当我在你的数据上试的时候,它输出了:

1 ===> 1
3 ===> 4
4 ===> 6

撰写回答