使用“diff”(或其他工具)获取文本文件之间的字符级差异

148
我希望使用'diff'工具获取行差异和字符差异。例如,考虑以下内容: 文件 1:

abcde
abc
abcccd

文件2

abcde
ab
abccc

使用diff -u,我得到:

@@ -1,3 +1,3 @@
 abcde
-abc
-abcccd
\ No newline at end of file
+ab
+abccc
\ No newline at end of file

然而,它只向我展示了这些行中的更改。我想要看到的是:

@@ -1,3 +1,3 @@
 abcde
-ab<ins>c</ins>
-abccc<ins>d</ins>
\ No newline at end of file
+ab
+abccc
\ No newline at end of file

你懂我的意思。

现在,我知道我可以使用其他引擎来标记/检查特定行的差异。但我宁愿使用一种工具来完成所有这些工作。


3
每个字符的差异在处理CJK文本时特别有用,因为在CJK文本中不会使用空格来分隔单词。 - 把友情留在无盐
16个回答

125

Git有一个单词级别的差异比较工具,将所有字符定义为单词可以有效地获得字符级别的差异。然而,换行符的变化被忽略

示例

像这样创建一个代码仓库:

mkdir chardifftest
cd chardifftest
git init
echo -e 'foobarbaz\ncatdog\nfox' > file
git add -A; git commit -m 1
echo -e 'fuobArbas\ncat\ndogfox' > file
git add -A; git commit -m 2

现在,执行git diff --word-diff=color --word-diff-regex=. master^ master命令,你将会得到以下内容:

git diff

请注意,字符级别上同时识别了添加和删除操作,而新行的添加和删除操作被忽略。
您也可以尝试以下其中之一:
git diff --word-diff=plain --word-diff-regex=. master^ master
git diff --word-diff=porcelain --word-diff-regex=. master^ master

147
您无需创建任何仓库,只需在文件系统的任何位置提供任意两个文件给 git diff 命令,它就能正常工作。您提供的命令对我来说非常有效,谢谢!git diff --word-diff=color --word-diff-regex=. file1 file2 - qwertzguy
5
非常有帮助!如果可以的话,作为一名软件开发人员我会点+1,作为作者/作家我会再点2次+1。与代码不同,代码行往往比较短,而在写论文或故事时,每个段落往往采用长的自动换行格式,这种特性使得差异实际上具有视觉上的用处。 - mtraceur
57
为了让@qwertzguys上面的回答在Git repo之外也能够使用,我需要添加--no-index选项。因此,命令应该是:git diff --no-index --word-diff=color --word-diff-regex=. file1 file2。请注意,本文不提供任何解释性内容。 - Nathan Bell
2
git diff在一般设置中不起作用: git diff --no-index --word-diff=color --word-diff-regex=. <(echo string1) <(echo string2),但这个能行:diff --color <(echo string1) <(echo string2)。 - mosh
3
对于稍微更紧凑的命令,“--word-diff=color --word-diff-regex=.” 可以替换为 “--color-words=.”。 - Cole
显示剩余4条评论

55

你可以使用:

diff -u f1 f2 |colordiff |diff-highlight

screenshot

colordiff是Ubuntu的一个软件包。您可以使用sudo apt-get install colordiff来安装它。

diff-highlight来自git(自版本2.9起)。它位于/usr/share/doc/git/contrib/diff-highlight/diff-highlight中。您可以将其放在$PATH中的某个位置。


10
colordiff 也可以在 Homebrew 上安装到 Mac 上: brew install colordiff - Emil Stenström
7
在 Mac 上,您可以在 $(brew --prefix git)/share/git-core/contrib/diff-highlight/diff-highlight 中找到 diff-highlight - StefanoP
7
如果您没有使用brew安装git - 可以使用python的pip来安装diff-highlight - pip install diff-highlight(即使通过brew安装了git,我仍然更喜欢这种方法)。 - Yaron U.
1
实际上,你可以跳过第一步diff,如果值得的话。对我来说,colordiff -u file1 file2 | diff-highlight 就可以了。 - Cole
太棒了,正是我所需要的。 - Smeterlink

26
Python的difflib非常棒,如果你想以编程方式进行比较。对于交互式使用,我使用vim的diff模式(非常容易使用:只需使用vimdiff a b命令调用vim)。我偶尔也会使用Beyond Compare,它几乎可以满足你对比较工具的所有期望。
我还没有看到任何有用的命令行工具来完成这个任务,但正如Will所指出的,difflib的示例代码可能会有所帮助。

1
哦...我希望有更标准化的东西(比如隐藏的命令行参数)。最让人困扰的是,我有Beyond Compare 2,它甚至支持将差异的文本输出到文件/控制台,但它仍然只包括行差异而不是字符差异。如果没有其他更好的选择,我会研究一下Python。 - VitalyB
8
谢谢介绍vimdiff,我很喜欢。但是默认的颜色对我来说不太清晰,不过我在https://dev59.com/dHI-5IYBdhLWcg3wFkSO 找到了解决办法。 - undefined

19
你可以在Solaris中使用cmp命令:

cmp

比较两个文件,如果它们不同,则会告诉你它们第一个不同的字节和行号。


2
cmp 在(至少一些)Linux 发行版上也是可用的。 - Jeff Evans
7
它也可以在Mac OS X上使用。 - Eric R. Rath
字符可以由多个字节组成,而 OP 要求进行可视化比较。 - Cees Timmerman
1
@CeesTimmerman:cmp 允许使用标志“-l -b”进行可视比较。 - Smar
1
cmp在发现第一个不同之后就会退出。 - Norman Ramsey

15

正如主答案的评论所说,您不必提交即可使用git diff:

git diff --no-index --word-diff=color --word-diff-regex=. file1 file2

enter image description here

绿色是第二个文件添加的字符。

红色是第一个文件添加的字符。


1
是的,这是最佳答案(至少截至今天),因为它是在任何系统上都最容易使用的(git 完全跨平台,并且现在几乎每个人都有它),而且远比其他方式更容易使用。 - adamency
2
你可能需要包含 --no-index 才能使其正常工作,正如主要答案中所评论的。 - Paulo Freitas
@PauloFreitas 实际上我在截图中有它,但不知何故我在命令中错过了,感谢指出,已编辑。 - Eduard Florinescu
1
如果您想将其导入到 less,您可以使用 less -R 以保留颜色。 - qwertzguy

11

Python有一个方便的库称为difflib,它可以帮助回答你的问题。

下面是使用difflib的两个单行代码,适用于不同版本的Python。

python3 -c 'import difflib, sys; \
  print("".join( \
    difflib.ndiff( \ 
      open(sys.argv[1]).readlines(),open(sys.argv[2]).readlines())))'
python2 -c 'import difflib, sys; \
  print "".join( \
    difflib.ndiff( \
      open(sys.argv[1]).readlines(), open(sys.argv[2]).readlines()))'

这些可能会作为shell别名方便使用,这样更容易在您的`${SHELL_NAME}rc`配置文件中移动。
$ alias char_diff="python2 -c 'import difflib, sys; print \"\".join(difflib.ndiff(open(sys.argv[1]).readlines(), open(sys.argv[2]).readlines()))'"
$ char_diff old_file new_file

需要提供更易读的版本,以便放入独立文件中。

#!/usr/bin/env python2
from __future__ import with_statement

import difflib
import sys

with open(sys.argv[1]) as old_f, open(sys.argv[2]) as new_f:
    old_lines, new_lines = old_f.readlines(), new_f.readlines()
diff = difflib.ndiff(old_lines, new_lines)
print ''.join(diff)

优秀的一行代码。如果有一个忽略未更改行的压缩输出就好了。 - user3002273

7

彩色,字级别的diff输出

下面的脚本和diff-highlight(git的一部分)可实现以下操作:

彩色diff截图

#!/bin/sh -eu

# Use diff-highlight to show word-level differences

diff -U3 --minimal "$@" |
  sed 's/^-/\x1b[1;31m-/;s/^+/\x1b[1;32m+/;s/^@/\x1b[1;34m@/;s/$/\x1b[0m/' |
  diff-highlight

(感谢@retracile的答案提供了sed的高亮显示)


它在Shell屏幕上显示良好的差异,但我该如何在GVim中查看该差异呢? - Hemant Sharma
2
这其实是一个gvim问题 :). command | gvim - 可以实现你想要的功能。 - Att Righ
供参考,diff-highlight似乎作为git的一部分包含在内,但未放置在您的路径中。在我的机器上,它位于/usr/share/doc/git/contrib/diff-highlight - Att Righ
损坏的链接。我该如何安装diff-highlight?似乎不在包管理器中。 - Trevor Hickey

6
cmp -l file1 file2 | wc

对我来说很有效。结果的最左边的数字表示不同字符的数量。


1
或者只需获取最左边的数字:cmp -l file1 file2 | wc -l - Tony

5
我也写了自己的脚本来使用最长公共子序列算法解决这个问题。
它的执行方式如下: JLDiff.py a.txt b.txt out.html 结果以HTML格式显示,带有红色和绿色的着色。处理较大的文件需要指数级别的时间,但这确实是一个真正的逐字符比较,而不是先按行检查。

我发现在pypy下,JLDiff运行速度更快。 - Joshua

4

Python的difflib可以实现这一点。

文档中包括一个例子命令行程序供您参考。

确切的格式不是您指定的,但要么解析ndiff样式输出,要么修改示例程序以生成您的记号都很容易。


谢谢!我会研究一下。我希望有更标准化的方法(比如隐藏的命令行参数)。但这个方法可能还可以。如果没有更标准化的方法,我会尝试使用Python(虽然似乎没有)。 - VitalyB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接