比较两个版本 · 免费

文本对比工具

粘贴旧版本和新版本,准确查看新增、删除和修改了什么:可逐行或逐词对比,可设置空白和大小写的处理方式,提供对齐视图和统一视图,还能生成统一格式补丁,直接复制到代码审查中。

在浏览器中比较 · 不上传任何内容
报告问题

文本对比工作区

两个窗格都是空的。在两边各粘贴一个版本即可比较。

对比工作台

粘贴两个版本

旧的放左边,新的放右边。两者都只保留在这个标签页中:不上传、不发送请求,也不在任何地方保存副本。

0 行 · 0 词 · 0 字符
0 行 · 0 词 · 0 字符
修改行内的细节
行配对阈值

只有当共享字符比例高于 0.30 时,删除的行和新增的行才会在内部相互比较。

视为相同

这些选项改变的是什么算作相同,而不是显示的内容。屏幕上和复制出的补丁中的每个字符都与你粘贴的完全一致。

没有差异
暂无

在每个窗格中各粘贴一个版本,比较结果就会显示在这里。

新增行数
0
删除行数
0
修改行数
0
未改动行数
0
新增词数
0
删除词数
0
新增字符数
0
删除字符数
0
比较结果

并排显示,左右对应

新增内容带加号和下划线,删除内容带减号和删除线,所以即使黑白打印或在没有颜色的情况下阅读,结果也清晰可辨。

两个窗格都是空的。在左边粘贴较早的版本,在右边粘贴较新的版本,或者点击载入示例,查看一对同时包含各类改动的发布说明:三行改写、一行重写、一个新行、一个新空行,以及一个不打开显示不可见字符就绝对看不出来的行尾空格。

统一格式补丁

同一比较结果,以补丁形式呈现

真正的块头、两侧的上下文行,以及相距较近时合并的块。这些都是 git 和 GNU diff 采用的约定。

一旦有可描述的内容,统一格式补丁就会显示在这里。

移动的块会出现两次

编辑脚本只有两个动词:删除和插入,没有“移动”。挪动一个段落会被表述为它在这里消失、在那里出现,所以一次操作会产生两块颜色。

最短并不唯一

比较“a b c”和“a c b”有两个同样短的答案,每个工具都按约定选其中一个。当 diff 把改动归到你没碰过的行上时,通常只是选了对同一改动的另一种描述。

有时空白才是关键

忽略空白适用于文章和重新排版的代码。但对 Python、YAML 和 Makefile 来说是错的,因为在这些文件里缩进和真正的制表符属于语法。在那里,只改空白的改动就是改动本身。

两段文本都只保留在这个标签页中。比较由在你自己电脑上运行的普通 JavaScript 完成:不上传任何内容,不发送任何请求,也不在任何地方保存副本,所以合同、草稿或含有密钥的配置文件在这里和在你的编辑器里一样安全。每一边最多 200,000 个字符或 10,000 行;超出搜索限制时,某个区域会被报告为一整块被另一块替换,页面也会如实说明,而不会假装答案是最小的。

工作原理

两段文本,以及连接二者的最省力的解释。

diff 不是差异清单,而是把第一段文本变成第二段所需的最少删除和插入操作。这个区别很实在。它解释了为什么移动过的段落会出现两次,为什么一行被编辑过的内容在技术上是一处删除紧挨着一处新增,以及为什么两个不同的工具会对同一处改动给出不同结果,却都是对的。本页对各行运行 Myers 算法,再在每对修改过的行内部做细化,并展示推导过程,包括不得不退而求其次、给出较粗略结果的时候。

  1. 01

    粘贴两个版本,各放一边

    旧版本放左边,新版本放右边。两者都不会上传:比较完全在这个标签页内进行,所以合同、草稿或配置文件里的 API 密钥都不会离开你的电脑。放反了?点一个按钮就能交换两边,解读也会随之反转。

  2. 02

    决定什么算作差异

    空白、字母大小写和空行,只有在你认定时才算差异。关闭其中任何一项,比较结果就会改变;屏幕上的文本则不会,因为这些选项只影响哪些内容被视为相同。粒度决定一行修改如何拆分:整行、逐词或逐字符。

  3. 03

    并排阅读,或直接拿走补丁

    对齐视图把两个版本放在带行号的对应栏中;统一视图则像代码审查那样把它们上下排列。无论哪种视图,新增内容都带加号和下划线,删除内容都带减号和删除线,所以即使黑白打印,结果也一目了然。统一格式补丁复制出来时带有真正的块头。

适用于草稿、合同、配置文件和代码审查

最小编辑脚本、逐词细节,以及真正能应用的补丁。

最短编辑脚本,而不是第一个猜测

比较使用 Myers 算法,它找的是长度最短的编辑脚本,而不是第一个看似合理的对齐方式,所以移动一句话不会导致后面的每一行都被报告为已修改。开头和结尾的相同部分在搜索开始前就会被去掉,这就是为什么在长文档中改一个词也能瞬间出结果。

两个层级,一个词就显示为一个词

先比较各行,然后把一行删除内容与相对的新增行在内部再比较一次。把“星期二”改成“星期四”,你看到的是一个词被高亮,而不是整段被划掉。第二轮比较取决于两行的相似程度,所以确实无关的行不会被强行配成一对。

选项改变的是比较方式,从不改动你的字符

忽略大小写或空白的做法,是比较规范化后的副本,同时显示原文。屏幕上和复制出的补丁中的每个字符都与你粘贴的完全一致:不会有任何一行在过程中被悄悄去掉首尾空白、转成小写或重新缩进。

带真正块头的统一格式补丁

补丁面板会输出规范的 `@@ -a,b +c,d @@` 块头,上下文行数可以设置,相距较近的块会合并,计数为 1 时省略逗号。这些都是 git 和 GNU diff 采用的约定,所以输出可以直接粘贴到代码审查中。

数字附带计算公式

新增、删除、修改和未改动的行数;增加和减少的词数与字符数;还有一个相似度百分比,会说明它是怎么算出来的,而不是要你直接相信。统计数据始终按词级别计算,所以切换修改行的显示方式绝不会改变这些数字。

在可能卡死的地方退而求其次

比较两段毫无共同之处的文本是平方级的工作量,所以编辑距离和总搜索量都设有上限。超过上限后,某个区域会被报告为一整块被另一块替换(结果仍然正确,只是不再最小),页面也会说明这一点,而不是悄悄给出一个更差的答案。

diff 常见问题

移动的块、看不见的换行符,以及 @@ 行在告诉你什么。

文本对比工具到底做了什么?+

它会算出把一段文本变成另一段的最省力的方法。可用的操作只有删除一行和插入一行,所以一处“差异”其实是编辑脚本中的一条记录:这一行去掉了,这一行加进来了,其余保持不变。这个视角能解释 diff 大部分让人意外的行为。它没有“编辑一行”的概念(编辑只是恰好相邻的一次删除和一次插入),也没有“移动一段”的概念,只知道一段在这里消失、在那里出现。本页在此之上增加了第二轮比较:当一处删除和一处插入足够相似、像是同一行的两个版本时,会在它们内部再比较一次,向你展示真正改动的词。

为什么算法要让编辑次数最少,而不是找出“那些”差异?+

因为根本不存在“那些”差异:只有关于一段文本如何变成另一段的种种解释,而且有无数种都与事实相符。你总可以把任何改动描述成“删除全部,再插入全部”,这没错,但毫无用处。有用的约束是最小化:选择编辑次数最少的那种解释,因为它让原文最多地保留在原位,因而最接近一个人实际做的事。Myers 在 1986 年提出的算法能找到这样的解释,耗时与输入规模乘以该脚本长度成正比,所以两个几乎相同的文件无论多长,都能在几毫秒内比较完毕。

最短编辑脚本是唯一的吗?+

不是,在和 diff 较真之前最好先知道这一点。把“a b c”和“a c b”比较:你可以删除 b 再把它插到 c 之后,也可以删除 c 再把它插到 b 之前。两种都是两次编辑,没有哪种更正确。每个 diff 工具都靠约定而不是洞察力来打破平局。本工具倾向于先报告删除再报告插入,并尽可能早地采用匹配。所以,当 diff 把一处改动算到你没碰过的行上时,它通常没有错,只是选了另一种同样简短的方式来描述同一处改动。Git 的 `--patience` 和 `--histogram` 算法正是为了让这种取舍在源代码上更符合人的直觉。

为什么我移动的一块内容显示为一处删除和一处插入?+

因为“移动”不是可用的操作之一。编辑脚本只有两个动词:删除和插入,所以挪动一个段落会被表述为它从旧位置移除、在新位置出现。一个感觉上只是一次操作的改动,因此出现了双倍的红色和绿色。有些工具会在事后加上移动检测,寻找与别处插入块相同的删除块,但这只是叠加在上面的启发式方法,并非算法本身的一部分,而且当移动的块同时被编辑过时就会出错。实用的办法是把移动和编辑分成不同的修订:先在一次修改中移动章节,下一次再改措辞,这样每次的 diff 都清晰可读。

为什么只改空白的改动会淹没 diff,什么时候忽略它们是个错误?+

因为每一行是作为一个完整字符串来比较的。重新缩进文件、把制表符换成空格,或者让编辑器在保存时去掉行尾空格,都会改变它碰到的每一行,所以在你让比较忽略空白之前,格式调整和真正的编辑是无法区分的。审阅文章或重新排版过的代码时,关闭空白比较是正确的做法。但凡空白属于语法的内容,这样做就是错的:Python 的缩进决定代码块结构,YAML 的缩进决定嵌套层级,Makefile 的规则命令行必须以真正的制表符开头,而不能是八个空格。在这些文件里,只改空白的改动并不是表面问题。它就是改动本身,把它隐藏起来就会隐藏一个真正的 bug。本页把这两种情况分开处理:“忽略行首行尾空白”放过行的两端,同时仍然报告缩进;“忽略所有空白”则全部放过。

为什么两个看起来一模一样的文件,每一行都显示为不同?+

几乎总是换行符的问题。Windows 用回车符加换行符来结束一行,Unix 和 macOS 只用换行符,而老式 Mac 软件只用回车符。这些字符是看不见的,所以一个经过 Windows 编辑器的文件,可能每一行都与原文件不同,在屏幕上却完全一样。本页会按这三种结束符拆分,并比较每行的内容,所以 CRLF 与 LF 不会造成一大片虚假的改动。不过它仍会告诉你两边使用了不同的换行符,因为这个差异是真实存在的,会影响 git、shell 脚本的 shebang 行,以及任何逐字节读取文件的程序。其他看不见的元凶还有从网页粘贴来的不换行空格,以及文件最开头的字节顺序标记。

统一格式补丁中的 @@ 行是什么意思?+

它是块头,告诉补丁程序后面这一块应该放在哪里。`@@ -12,7 +12,9 @@` 的意思是:从原文件第 12 行开始,这里描述了七行;从新文件第 12 行开始,这里描述了九行。减号范围总是指原文件,加号范围指修订版,而且计数包括两侧显示的未改动上下文行,不只是改动的行。计数为 1 时省略逗号,空范围则相对于它所跟随的那一行来写,所以在第 4 行之后的纯插入会显示为 `-4,0`。块头下方,行首为空格表示未改动,减号表示删除,加号表示新增。正是上下文让补丁能够应用到略有变化的文件上:两侧各有三行上下文时,即使上方有无关的编辑,补丁程序仍能找到正确的位置。

为什么逐词高亮需要相似度阈值?+

因为把一行删除内容和一行插入内容配对是一种猜测,而错误的猜测比不猜更糟。如果两行确实是彼此的不同版本,在内部比较它们正是你想要的。如果它们只是恰好相邻,内部比较就会找出它们共有的几个短词和零散标点,结果碎成一地:散落的红绿碎片,比“这一行去掉了,这一行加进来了”更难读。阈值是一个比率:两行共有的字符数(两边都计入)除以两行的总长度。1 表示完全相同,0 表示毫无共同之处。高于你选择的设置时,这对行会被细化并逐词显示;低于设置时,两行会被报告为直接替换。比较大幅改写的文章时可以放宽,当 diff 开始看起来像一堆彩带时就收紧。

两段文本最多能有多大,达到上限时会怎样?+

每一边最多 200,000 个字符或 10,000 行,大约相当于一部 40,000 词的书稿。在此范围内还有两个较宽松的限制。当编辑脚本将超过 12,000 次编辑,或搜索超出其计算预算时,行比较就会停止搜索,因为比较两段毫无共同之处的文本是平方级的工作量,否则会卡死标签页;超过这一点后,受影响的区域会被报告为一整块被另一块替换,并有提示说明。另外,超过 4,000 个词或字符的单行只会按相同的开头和结尾进行细化,而不是完整细化,这同样会被标注出来,不会隐藏。这些限制都会给出正确的结果,只是比平时更粗略。

它能告诉我两段代码的功能是否相同吗?+

不能,任何 diff 工具都做不到。它比较的是文本,不是含义。一致地重命名一个变量、调换两个互不相关的函数定义的顺序、把单引号换成双引号,或者把参数列表重新排成三行,都会产生很大的 diff,行为却毫无变化。反过来,`>` 变成 `>=` 只差一个字符,却可能正是整个 bug 所在。diff 记录的是输入了什么,而不是会发生什么,这正是代码审查要先看 diff 再跑测试的原因。如果你想要理解结构而不是字符的比较,就需要一个能解析该语言的工具,而在这里有用的第一步,是在比较之前统一两边的格式,让 diff 反映内容而不是排版。

更多专注好用的工具,随时待命。

探索不断扩充的工具合集,涵盖计算、文档、写作和日常工作。

浏览全部工具