文本差异对比

全球通用工具

粘贴任意内容的两版——一段修改前后的文字、一个配置文件、一份合同、一份名单——页面会显示它们之间发生了什么变化:默认逐行,也可以逐词;既能给出可复制的统一差异,也能给出变更表格,列出每条被改写的行里究竟哪些词发生了移动。两段文本都留在浏览器中,比较结果随每次按键重新绘制。

所有处理都在当前标签页中随输入实时完成。允许留空:此时答案就是一段全部新增的文本。

两个输入框都为空,是本页唯一拒绝的输入,因为任何可以打印的答案都会是一份空差异。

逐行是评审时阅读的格式,也是差异下载文件采用的格式。逐词则把变化标在句子内部,适合段落级修改。

`Price` 和 `price` 对读文章的人是同一行,对比较配置文件的人却是不同行,所以这里做成开关,而不是替你决定。

忽略空格会在比较前把每一段连续空格和制表符压成一个空格。页面打印的文本仍然完全是你粘贴的原样。

两段文本只有空行排布不同,是最常见的误报。忽略空行会从两侧同时丢弃它们,因此行号会变成剩余行的编号。

每个变更两侧打印多少行未变内容。`git diff` 默认用 3;设为 0 时只打印变更本身。

留空时使用 `text-diff.txt` 和 `text-diff-changes.csv`。

边输入边更新。所有计算都在你的浏览器里完成,数据不会离开本机。

货币符号

只改变符号,不做汇率换算:数字本身没有转换。

需要阅读的变更

4

新增 3 行,删除 3,改写 2,分布在 2 个差异块中。两段文本有 25% 相同。

相同度
25% 共享单位占两段文本全部单位的比例
未变
1 两段文本中顺序一致的单位
新增
3 只存在于第二段文本
删除
3 只存在于第一段文本
改写
2 一次删除配一次添加
第一段 / 第二段
4 / 4 25 个词与 24 个词

两段文本,逐行对照

第一段文本第二段文本
-Shipping is free on orders over $50.
+Shipping is free on orders over $75.
-Orders are sent within two working days.
+Orders are sent within one working day.
-Returns are accepted for 30 days.
Custom items cannot be returned.Custom items cannot be returned.
+Gift cards are not refundable.
全部 7 个单位,按顺序。
空行与其他行一样是一行,所以在中间增加一个空行也会显示为变更。

`-` 表示只有第一段文本才有的单位,`+` 表示只有第二段才有的单位,空白标记表示两边都有。被改写的行会在这里出现两次,每侧一次,这正是评审需要看到的。

改了什么,每处变更一行

变更位置第一段文本第二段文本离开的词到达的词
改写第 1 行 改为 第 1 行Shipping is free on orders over $50.Shipping is free on orders over $75.5075
改写第 2 行 改为 第 2 行Orders are sent within two working days.Orders are sent within one working day.two daysone day
删除第 3 行Returns are accepted for 30 days.Returns are accepted for 30 days
新增第 4 行Gift cards are not refundable.Gift cards are not refundable
全部 4 处变更。
比较设置:逐行比较,所有差异都计入。

`离开的词` 与 `到达的词` 是集合差异,而不是对齐结果:它们回答“这一行有哪些词进出了”,并不假装知道哪个词替换了哪个词。只改变大小写的词也会被算作离开和到达,除非正在忽略大小写。

统一差异

--- 第一段文本
+++ 第二段文本
@@ -1,4 +1,4 @@
-Shipping is free on orders over $50.
+Shipping is free on orders over $75.
-Orders are sent within two working days.
+Orders are sent within one working day.
-Returns are accepted for 30 days.
 Custom items cannot be returned.
+Gift cards are not refundable.

可以复制,也可以下载。`-` 表示只有第一段文本才有的行,`+` 表示只有第二段才有的行,空格是上下文,`@@` 头部里的数字是你粘贴的两段文本的行号。它读起来像补丁,但并不是补丁:补丁需要原文件的精确字节。

这些数字没有说明的事

  • 算法是 patience diff:先找出在两侧都只出现一次的单位,再取它们在两侧顺序一致的最长递增序列作为锚点,锚点之间的空隙递归套用同一规则。这就是 `git diff --patience` 使用的算法,也是文档中间一行被改写时会得到一行变更、而不是一整面变更的原因。
  • 其他人不会说明的决定:当两侧的某一段都没有“各出现一次”的单位时,就没有东西可以用来对齐,于是本页按位置配对,并把整段报告为替换,而不是猜测哪一行变成了哪一行。重复行 —— 孤立的 `}` 或空行都很常见 —— 有意不作为锚点,原因相同:它出现不止一次,无法说明两段文本在哪里对齐。
  • 被移动的行会报告为一次删除加一次添加,而不是“移动”。检测移动是另一种算法,有自己的意外;声称找到了无法证明的移动,比诚实展示两半更糟。
  • 这里的相似度是 25%,即共享单位占两段文本全部单位的比例 —— Dice 系数,100% 表示完全相同,50% 表示任一侧有半数单位被共享。比较覆盖 4 与 4 个单位:增加了一段落的文本并不是“90% 相似”,而是增加了一段落的文本,百分比旁边的计数才是应该读的数字。
  • 有意不提供:三方合并、冲突解决、语法高亮,以及“接受此变更”按钮 —— 那些属于能够写回文件的编辑器,而本页从不接触文件。两个下载分别是差异本身和变更清单,都不是能干净应用的补丁,因为补丁需要原文件的精确字节,而不是输入框里的文本。
比较在当前标签页中完成,执行它的是生成页面里已有差异的同一个函数,因此没有向任何地方发送内容 —— 页面背后也没有可以接收两份文档的端点,即使有东西尝试发送也不行。正在谈判的合同可以在这里粘贴。

diff 看起来简单,实际上并不简单,这也是两个工具会对同一对文本给出不同结论的原因。本页把三个决定明确说出来:比较单位是什么,一行还是一个词;两个单位在什么条件下算相同,大小写和空格是开关而不是预设;两侧对不齐时如何配对。第三个决定很少有工具解释,本页会正面回答,而不是把它藏起来。

关于这个工具

找出两版草稿之间的改动听起来像是已经解决的问题,但两个 diff 工具对同一对文本给出不同结论是常事。本页展示究竟移动了什么——按行或按词,给出可复制的统一差异和变更表格,并列出每条被改写行中离开与到达的词——同时在每次按键后重绘比较结果。

分歧来自三个决定,本页把三个都摆到明面上:比较单位是什么;两个单位何时算相同;两侧对不齐时如何对齐。前两个是大小写和空格开关;第三个是多数工具隐藏的对齐规则,这里会写清楚,而不是埋在输出里。

这里给出的是比较结果,不是合并结果:页面告诉你改了什么,但不会替你写出合并后的文件。两段文本都留在浏览器中,整个流程在客户端运行,因此合同、配置或私人笔记都可以在不把任一版本发送到服务器的前提下完成比较。

它是怎么工作的

diff 真正决定的是什么

diff 的难点不在于发现某一行变了,而在于判断第一段文本中的哪一行对应第二段中的哪一行。一个在开头附近插入了段落的文档,并不是“后面所有内容都变了”的文档;配对糟糕的 diff 会显示一百行变更,而人眼看到的只是一次插入。

本页使用 **patience diff**,也就是 `git diff --patience` 采用的算法。它先找出在两侧都只出现一次的单位——某一行在两段文本中各出现一次,几乎可以确定就是同一行——再取这些单位在两侧顺序一致的最长递增子序列作为锚点,锚点之间的区间递归套用同一规则。作为背景,传统 diff 常基于最长公共子序列做动态规划,复杂度与两侧长度乘积相关;Myers 算法按编辑距离推进,在差异较少时更快;patience 则借唯一行避开重复行造成的错误配对。三者的取舍不同,但对“插入一段”这类常见修改,锚点法通常更接近人的判断。

没人解释的那个决定,以及本页的处理方式

迟早会遇到两侧都没有任何锚点的一段文本:其中没有任何内容在两边各出现一次,也就没有证据说明两侧如何对齐。每个 diff 工具都必须在这里做点什么,多数工具却不说明做了什么。本页按位置配对,并把整段报告为替换,这种做法诚实且可预测:删掉两行再写两行不同的内容,会得到两次改写,而不是猜测哪一行变成了哪一行。

同样的规则解释了为什么重复行不能当锚点:孤立的右花括号、空行、一连串相同的表格行,都出现不止一次,命中其中一个并不能说明两侧真正对齐的位置。被移动的行会显示为一次删除加一次添加,而不是“移动”,因为检测移动是另一种算法,有自己的误判方式;声称一个无法证明的移动,比老老实实展示两半更糟。此外,行尾空格、制表符与空格的差异,以及 CRLF 与 LF 的差异,都会让两行在字节层面不相等,从而产生看似莫名其妙的假变更;对应开关可以把这类噪声排除在判断之外。

同一个答案的两种读法

逐行模式给出评审时习惯阅读的格式:`-` 表示只有第一段文本才有的行,`+` 表示只有第二段才有的行,空格表示变更周围的上下文,`@@ -12,7 +12,8 @@` 这样的头部则说明该差异块在两侧各覆盖哪些行。下载的 diff 文件就是这种格式,也适合直接贴进工单或聊天窗口。

逐词模式只打印**一遍**文本,并在其中标出变化,例如 `shipping is free over [-$50.-]{+$75.+}`。段落级修改更需要这种读法,因为阅读两段长句的统一差异,等于要在两行几乎相同的内容里找一个词。这里的单位是一段非空白字符或一段空白字符,所以标点会跟随它所依附的词,标记也不会凭空造出词边界。

开关做什么,不做什么

大小写和空格被做成开关,因为两种答案对某些人都是对的:`Price` 和 `price` 对普通读者是同一行,对配置文件却是不同行;一个空格变成两个,对读代码的人是变更,对读文章的人是噪声。忽略其中任何一项,只改变“什么算差异”,页面上打印的文本始终是你粘贴的原样,因此可以核对开关的效果,而不是只能相信它。

忽略空行这个开关能消除最常见的误报——两段文本只是空行排布不同——但它有明确代价:空行会从两侧同时丢弃,表格里的行号随之变成剩余行的编号。上下文设置决定每个变更周围打印多少行未变内容,对应 `git diff -U` 的行为;设为 0 时就只剩变更本身。

示例

示例使用页面自带的默认输入,只改设置;下面的结果与你在页面上看到的完全一致。

句子中改动一个词

第一段文本(原始版本): Shipping is free on orders over $50. · 第二段文本(修改后的版本): Shipping is free on orders over $75. · 比较方式: 逐词比较,适合段落修改

需要阅读的变更

1

新增 1 个词,删除 1,改写 1,分布在 1 个差异块中。两段文本有 92.3% 相同。

相同度: 92.3% · 未变: 12 · 新增: 1 · 删除: 1

词级模式把变化标在句子内部;若把同一处修改读成统一差异,就得在两行几乎相同的内容里注意到那一个词。

插入一段,其余内容不算变更

第一段文本(原始版本): The first line. The last line. · 第二段文本(修改后的版本): The first line. A new line in the middle. The last line.

需要阅读的变更

1

新增 1 行,删除 0,改写 0,分布在 1 个差异块中。两段文本有 80% 相同。

相同度: 80% · 未变: 2 · 新增: 1 · 删除: 0

两行未变内容成为锚点,因此答案是一次添加,而不是三行变更——配对糟糕的 diff 恰好会打印出后者。

同一段文本,空格排布不同

第一段文本(原始版本): alpha alpha bravo · 第二段文本(修改后的版本): alpha alpha bravo · 空行: 忽略差异:两侧都丢弃空行

没有差异

0

两段文本逐行比较,忽略空行,完全相同。两边每个单位都与对方一一对应,顺序一致。

相同度: 100% · 未变: 3 · 新增: 0 · 删除: 0

忽略空行后没有任何可报告的内容,这是对最常见误报最诚实的回答。

一侧内容全部是新的

第一段文本(原始版本): — · 第二段文本(修改后的版本): A first line. A second line.

需要阅读的变更

2

新增 2 行,删除 0,改写 0,分布在 1 个差异块中。两段文本有 0% 相同。

相同度: 0% · 未变: 0 · 新增: 2 · 删除: 0

一个输入框为空、另一个有内容是允许的,结果会按“整段文本都是新增”处理:两次添加,别无其他。

常见问题

粘贴的内容会被发送到某处吗?

不会。比较按同一条固定规则处理两个字符串:它在你输入时于浏览器中运行,页面里按同一条规则跑一遍,提前生成页面里已有的差异。没有上传按钮,因为不存在上传;页面背后也没有可以接收两份文档的端点,即使有东西尝试发送也不行。正在谈判的合同可以在这里粘贴。

为什么移动一段会显示为删除加添加?

因为这是算法能够证明的内容。检测移动意味着找出在两段文本中以不同位置出现的连续行,那是另一种算法,有自己的失败模式;多数声称能检测移动的工具,靠的是一个猜测出来的相似度阈值。本页诚实地展示两半并说明原因;移动视图属于真正持有文件的编辑器。

为什么两行改动被报告成四处变更?

先检查空行和空格开关,再读并排视图:看起来相同的行,如果在行尾空格、制表符与空格、或大小写上不同,在当前设置下就是不同的行。每条被改写的行还会列出离开和到达的词,通常足以看出是哪个不可见字符被计入了。如果那只是你不在意的空格差异,空格开关就是答案。

这个差异是可以应用的补丁吗?

不是,页面也会明确说明。它是读者认得出的统一差异:`-`、`+`、上下文以及 `@@ -start,count +start,count @@` 头部都是真实的。能干净应用的补丁需要原文件的精确字节——文件名、行尾格式、文件模式——而这里只有两个粘贴文本框,没有文件;把下载内容叫补丁,第一次遇到网页粘贴导致行尾被规范化时就会变成谎言。

相似度百分比是怎么算出来的?

它是共享单位占两侧全部单位的比例,即 Dice 系数 `2 x shared / (first + second)`。完全相同的文本是 100%,完全不同的两段是 0%;给长文档增加一段,数字只会移动几个百分点,这是算术造成的,而不是文档变得更不相似。计数会印在百分比旁边,原因就在这里:百分比是摘要,计数才是答案。

词级模式里什么算一个单位?

一段非空白字符,或一段空白字符。因此 `$50.` 连同句号算一个单位,撇号留在所属单词内部,从文字处理器粘贴来的弯引号也会跟随它依附的词。标记在这个粒度上是诚实的:它显示哪段字符发生了变化,不会在未检测语言的情况下擅自判断什么是一个“词”。

能比较两段以上的文本吗?

这个页面不能。三方合并是另一种工具,答案也不同——它要算出每一侧相对于共同祖先改了什么,再决定两侧改了同一处时怎么办——做得很差还不如不提供。这里一次比较两个版本;需要事后讨论差异时,可以下载变更清单。

能处理很长的文本吗?

能,算法正是为此选择的:patience diff 先切掉公共前缀和后缀,再沿锚点推进,因此只有少量修改的长文档很快;逐行重写的文档代价更高,但开销与锚点数量相关,而不是随行数平方增长。表格会限制打印数量——四十处变更、六十行——两个下载文件则始终完整。

资料来源与统计口径

  • 比较是逐行差异,使用与 git 相同的 `@@` 头部,另可选按词比较。
  • 开关只改变“什么算差异”,不改变打印内容:忽略大小写时,`Price: 10` 与 `price: 10` 是同一行。
  • 页面不做三方合并,也不判断哪个版本更好;两段文本只留在浏览器里,不会上传。

最近复核于 2026-09-17.

本页在您的浏览器本地运行,请保留原文件备份;不涉及财务建议。 阅读完整免责声明。