文本差异对比

并排比较两个文本,逐行高亮显示差异

加载示例

文本 A

文本 B

本地运行 · 粘贴密钥安全无忧
本地运行 · 粘贴密钥安全无忧

什么是文本 Diff?

文本 diff(difference 的缩写)是对两段文本进行比较后得出的结果,用于识别哪些行被添加、删除或保持不变。这一概念源自 Unix diff 工具,该工具于 1974 年作为 Unix 第 5 版的一部分首次发布。如今,文本 diff 已成为 Git 等版本控制系统的核心机制——每次提交存储的是 diff,而非每个文件的完整副本。

diff 算法通过寻找两个行序列之间的最长公共子序列(LCS)来工作。属于 LCS 的行标记为未更改;存在于原始文本但不在 LCS 中的行标记为已删除;存在于修改后文本但不在 LCS 中的行标记为已添加。最终得到将一段文本转换为另一段所需的最小变更集。

diff 输出有多种格式。统一 diff(unified diff,git diff 的默认格式)用减号前缀标记已删除行,用加号前缀标记已添加行。并排 diff 将两段文本排列在平行列中。本工具采用逐行比较并以颜色编码输出:绿色表示新增,红色表示删除,中性色表示未更改。未更改的行默认不带前缀显示,也可以隐藏,以便只关注变更内容。

为什么使用在线文本 Diff 工具?

在终端中比较文本需要安装 diff 工具并处理命令行参数。基于浏览器的 diff 工具完全消除了这些障碍。

即时比较
粘贴两段文本,立即看到高亮显示的差异。无需创建文件,无需记忆命令,无需解析输出。
🔒
隐私优先处理
所有比较均在浏览器中通过 JavaScript 完成。您的文本不会离开您的设备,这在对比配置文件、凭据或专有代码时尤为重要。
📋
可直接复制的输出
diff 输出使用标准的 + / - 前缀,与统一 diff 格式一致。您可以将结果直接复制到提交信息、代码审查评论或缺陷报告中。
🌐
无需登录或安装
在任何有浏览器的设备上均可使用。无需创建账户,无需安装扩展或桌面应用,打开页面即可开始比较。

文本 Diff 使用场景

前端开发
在构建步骤前后比较压缩后的 CSS 或 HTML 输出,以发现生成的标记或样式中的意外变更。
后端工程
对比不同环境(预发布 vs 生产)的 API 响应,验证部署是否引入了意外的数据变化。
DevOps 与基础设施
在将变更应用到线上集群或服务器之前,比较 Kubernetes 清单、Terraform 计划或 Nginx 配置文件。
QA 与测试
将实际测试输出与存储的快照文件进行 diff,验证测试结果是否符合预期基准。
数据工程
在数据库迁移过程中对比 CSV 列头或 SQL 模式定义,确认列是否被正确添加或重命名。
学习与课程作业
将您的解题方案与参考实现逐行比较,找出逻辑与预期思路的差异所在。

Diff 输出格式对比

Diff 工具会以多种格式产生输出。下表汇总了最常见的几种格式、生成工具及适用场景。

格式工具 / 来源说明
Unified diffdiff -u / git diffPrefixes lines with + / - / space; includes @@ hunk headers
Side-by-sidediff -y / sdiffTwo columns, changed lines aligned horizontally
Context diffdiff -cShows changed lines with surrounding context, marked with ! / + / -
HTML diffPython difflibColor-coded HTML table with inline change highlights
JSON PatchRFC 6902Array of add/remove/replace operations on a JSON document

逐行 Diff 的工作原理:LCS 算法

大多数逐行 diff 工具(包括本工具)使用最长公共子序列(LCS)算法。LCS 找出在两段文本中以相同相对顺序出现的最大行集合,无需要求这些行连续。不在 LCS 中的行即为实际差异。

标准 LCS 算法使用动态规划,时间复杂度为 O(m x n),其中 m 和 n 分别是两段文本的行数。对于大文件,Myers' diff 算法(Git 所采用)等优化变体将其降低至 O(n + d²),其中 d 为差异数量,在大多数行相同时速度极快。

1. 构建 DP 表
创建一个 (m+1) x (n+1) 矩阵。对于每对行,存储迄今为止最长公共子序列的长度。相等的行在对角线值上加 1。
2. 回溯
从矩阵右下角回溯至 (0,0)。相等行上的对角线移动产生"未更改"条目;水平或垂直移动产生"已添加"或"已删除"条目。
3. 渲染输出
将每个条目映射为显示行:未更改行不带前缀,已添加行加 +,已删除行加 -。应用颜色编码以增强视觉清晰度。

代码示例

以下是 JavaScript、Python、Go 和命令行中逐行文本比较的实现示例,每个示例均生成统一 diff 风格的输出。

JavaScript
// Line-by-line diff using the LCS algorithm
function diffLines(a, b) {
  const linesA = a.split('\n')
  const linesB = b.split('\n')

  // Build LCS table
  const m = linesA.length, n = linesB.length
  const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0))
  for (let i = 1; i <= m; i++)
    for (let j = 1; j <= n; j++)
      dp[i][j] = linesA[i-1] === linesB[j-1]
        ? dp[i-1][j-1] + 1
        : Math.max(dp[i-1][j], dp[i][j-1])

  // Backtrack to produce diff
  const result = []
  let i = m, j = n
  while (i > 0 || j > 0) {
    if (i > 0 && j > 0 && linesA[i-1] === linesB[j-1]) {
      result.unshift({ type: 'equal', text: linesA[i-1] }); i--; j--
    } else if (j > 0 && (i === 0 || dp[i][j-1] >= dp[i-1][j])) {
      result.unshift({ type: 'add', text: linesB[j-1] }); j--
    } else {
      result.unshift({ type: 'remove', text: linesA[i-1] }); i--
    }
  }
  return result
}

const diff = diffLines("alpha\nbeta\ngamma", "alpha\nbeta changed\ngamma\ndelta")
// → [
//   { type: 'equal',  text: 'alpha' },
//   { type: 'remove', text: 'beta' },
//   { type: 'add',    text: 'beta changed' },
//   { type: 'equal',  text: 'gamma' },
//   { type: 'add',    text: 'delta' }
// ]
Python
import difflib

text_a = """alpha
beta
gamma""".splitlines()

text_b = """alpha
beta changed
gamma
delta""".splitlines()

# Unified diff (same format as git diff)
for line in difflib.unified_diff(text_a, text_b, fromfile='a.txt', tofile='b.txt', lineterm=''):
    print(line)
# --- a.txt
# +++ b.txt
# @@ -1,3 +1,4 @@
#  alpha
# -beta
# +beta changed
#  gamma
# +delta

# HTML side-by-side diff
d = difflib.HtmlDiff()
html = d.make_file(text_a, text_b, fromdesc='Original', todesc='Modified')
Go
package main

import (
	"fmt"
	"strings"
)

// Minimal LCS-based line diff
func diffLines(a, b string) {
	la := strings.Split(a, "\n")
	lb := strings.Split(b, "\n")
	m, n := len(la), len(lb)

	dp := make([][]int, m+1)
	for i := range dp {
		dp[i] = make([]int, n+1)
	}
	for i := 1; i <= m; i++ {
		for j := 1; j <= n; j++ {
			if la[i-1] == lb[j-1] {
				dp[i][j] = dp[i-1][j-1] + 1
			} else if dp[i-1][j] >= dp[i][j-1] {
				dp[i][j] = dp[i-1][j]
			} else {
				dp[i][j] = dp[i][j-1]
			}
		}
	}

	var result []string
	i, j := m, n
	for i > 0 || j > 0 {
		if i > 0 && j > 0 && la[i-1] == lb[j-1] {
			result = append([]string{" " + la[i-1]}, result...)
			i--; j--
		} else if j > 0 && (i == 0 || dp[i][j-1] >= dp[i-1][j]) {
			result = append([]string{"+" + lb[j-1]}, result...)
			j--
		} else {
			result = append([]string{"-" + la[i-1]}, result...)
			i--
		}
	}

	for _, line := range result {
		fmt.Println(line)
	}
}

// Output:
//  alpha
// -beta
// +beta changed
//  gamma
// +delta
CLI (diff / git)
# Compare two files with unified diff (3 lines of context)
diff -u original.txt modified.txt

# Git diff between working tree and last commit
git diff HEAD -- file.txt

# Git diff between two branches
git diff main..feature -- src/

# Side-by-side diff in the terminal
diff -y --width=120 original.txt modified.txt

# Color-coded diff (requires colordiff)
diff -u original.txt modified.txt | colordiff

常见问题

逐行 diff 与字符 diff 有什么区别?
逐行 diff 逐行比较文本:若某行中有任意字符发生变化,整行都会被标记为已删除,新版本标记为已添加。字符 diff(或词级 diff)粒度更细,标记行内发生变化的具体字符或单词。逐行 diff 是代码审查的标准方式;字符 diff 更适合散文编辑。
git diff 内部是如何工作的?
Git 默认使用 Myers' diff 算法,该算法寻找两个文件之间的最短编辑脚本(最少插入和删除次数)。Git 将结果以统一 diff 格式存储,并附带指示行号的 @@ 块头。运行 git diff 时,Git 比较工作区与索引(暂存区),而非上一次提交,除非明确传入 HEAD。
可以用文本 diff 工具对比二进制文件吗?
不可以。文本 diff 工具按换行符分割输入并比较字符串。二进制文件包含任意字节序列,会产生无意义的行分割。如需比较二进制文件,请使用十六进制 diff 工具或文件级校验和比较(对两个文件分别执行 sha256sum)。
使用本工具时数据会发送到服务器吗?
不会。本工具完全在浏览器中运行。diff 计算使用在客户端执行的 JavaScript LCS 实现,不会发出任何携带您文本内容的网络请求。您可以在使用工具时打开浏览器的"网络"选项卡来验证这一点。
什么是统一 diff 格式?
统一 diff 是 diff -u 和 git diff 使用的输出格式。它以 -(已删除)和 +(已添加)前缀显示变更行,前面有 @@ 头部说明两个文件中的行号范围。上下文行(未更改行)带有空格前缀。统一 diff 是补丁、Pull Request 和代码审查工具中最常见的格式。
如何比较有大量差异的大文件?
对于拥有数千行的文件,基于浏览器的工具可以运行,但可能因 LCS 算法的二次方复杂度而变慢。对于非常大的 diff,diff 或 git diff 等命令行工具更快,因为它们使用优化的 C 实现并可以流式输出。您也可以关闭"显示未更改的行"选项,只专注于变更内容。
什么是三路合并 diff?
三路合并将一个文件的两个修改版本与它们的共同祖先(基础版本)进行比较。Git 在 git merge 和 git rebase 时使用此方法。它识别每个分支相对于基础版本所做的更改并将其合并。当两个分支修改了同一行时,Git 会报告合并冲突。三路合并需要三个输入,而标准 diff 只需要两个。