ToolDeck

So Sánh Văn Bản

So sánh hai đoạn văn bản cạnh nhau và đánh dấu sự khác biệt từng dòng

Thử ví dụ

Văn Bản A

Văn Bản B

Chạy cục bộ · An toàn để dán thông tin bí mật
Chạy cục bộ · An toàn để dán thông tin bí mật

Text Diff là gì?

Text diff (viết tắt của "difference" — sự khác biệt) là kết quả của việc so sánh hai khối văn bản và xác định những dòng nào được thêm, xóa hoặc giữ nguyên. Khái niệm này bắt nguồn từ tiện ích Unix diff, được phát hành lần đầu vào năm 1974 trong Version 5 Unix. Ngày nay, text diff là nền tảng của các hệ thống quản lý phiên bản như Git, nơi mỗi commit lưu trữ một diff thay vì toàn bộ bản sao của từng tệp.

Thuật toán diff tìm Dãy Con Chung Dài Nhất (Longest Common Subsequence — LCS) giữa hai chuỗi dòng. Các dòng có trong LCS được đánh dấu là không thay đổi. Các dòng trong văn bản gốc nhưng không có trong LCS được đánh dấu là đã xóa. Các dòng trong văn bản đã sửa đổi nhưng không có trong LCS được đánh dấu là đã thêm. Kết quả là tập hợp các thay đổi tối thiểu cần thiết để chuyển đổi văn bản này thành văn bản kia.

Đầu ra diff có nhiều định dạng. Unified diff (mặc định cho git diff) thêm dấu trừ trước các dòng bị xóa và dấu cộng trước các dòng được thêm. Side-by-side diff sắp xếp cả hai văn bản trong các cột song song. Công cụ này sử dụng so sánh từng dòng với đầu ra được tô màu: xanh lá cho các dòng thêm, đỏ cho các dòng xóa, và màu trung tính cho các dòng không đổi. Các dòng không đổi được hiển thị mặc định nhưng có thể ẩn đi để tập trung vào những gì đã thay đổi.

Tại sao dùng công cụ Text Diff online?

So sánh văn bản trong terminal đòi hỏi phải cài đặt tiện ích diff và xử lý các cờ dòng lệnh. Công cụ diff trên trình duyệt loại bỏ hoàn toàn sự phức tạp đó.

So sánh tức thì
Dán hai đoạn văn bản và xem sự khác biệt được đánh dấu ngay lập tức. Không cần tạo tệp, không cần nhớ lệnh, không cần phân tích đầu ra.
🔒
Xử lý bảo mật dữ liệu
Toàn bộ quá trình so sánh diễn ra trong trình duyệt của bạn bằng JavaScript. Văn bản của bạn không bao giờ rời khỏi thiết bị, điều này quan trọng khi so sánh các tệp cấu hình, thông tin xác thực hoặc mã nguồn độc quyền.
📋
Đầu ra sẵn sàng để sao chép
Đầu ra diff sử dụng tiền tố + / - chuẩn khớp với định dạng unified diff. Bạn có thể sao chép kết quả trực tiếp vào thông điệp commit, bình luận code review hoặc báo cáo lỗi.
🌐
Không cần đăng nhập hay cài đặt
Hoạt động trên bất kỳ thiết bị nào có trình duyệt. Không cần tạo tài khoản, không cần extension, không cần ứng dụng desktop. Mở trang và bắt đầu so sánh ngay.

Các trường hợp sử dụng Text Diff

Phát triển Frontend
So sánh đầu ra CSS hoặc HTML đã nén trước và sau bước build để phát hiện các thay đổi không mong muốn trong markup hoặc style được tạo ra.
Lập trình Backend
So sánh phản hồi API giữa các môi trường (staging và production) để xác nhận rằng một lần triển khai không tạo ra các thay đổi dữ liệu bất ngờ.
DevOps & Hạ tầng
So sánh các manifest Kubernetes, kế hoạch Terraform hoặc cấu hình Nginx trước khi áp dụng thay đổi lên cụm hoặc máy chủ đang chạy.
QA & Kiểm thử
Xác nhận rằng đầu ra kiểm thử khớp với baseline kỳ vọng bằng cách so sánh kết quả thực tế với tệp snapshot đã lưu.
Kỹ thuật dữ liệu
So sánh tiêu đề CSV hoặc định nghĩa schema SQL qua các lần migration cơ sở dữ liệu để xác nhận các cột đã được thêm hoặc đổi tên đúng cách.
Học tập & Bài tập
So sánh giải pháp của bạn với một cài đặt tham chiếu từng dòng để tìm nơi logic của bạn khác với cách tiếp cận dự kiến.

So sánh các định dạng đầu ra Diff

Các công cụ diff tạo ra đầu ra ở nhiều định dạng khác nhau. Bảng dưới đây tóm tắt các định dạng phổ biến nhất, công cụ nào tạo ra chúng và khi nào mỗi định dạng hữu ích.

Định dạngCông cụ / NguồnMô tả
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

Cách Line Diff hoạt động: Thuật toán LCS

Hầu hết các công cụ line-diff, bao gồm công cụ này, đều sử dụng thuật toán Longest Common Subsequence (LCS). LCS tìm tập hợp dòng lớn nhất xuất hiện trong cả hai văn bản theo cùng thứ tự tương đối, mà không yêu cầu chúng liền kề nhau. Các dòng không có trong LCS chính là sự khác biệt thực sự.

Thuật toán LCS chuẩn sử dụng quy hoạch động và chạy trong thời gian O(m x n), trong đó m và n là số dòng của hai văn bản. Đối với các tệp lớn, các biến thể tối ưu hóa như thuật toán Myers' diff (được Git sử dụng) giảm xuống còn O(n + d^2) trong đó d là số lượng sự khác biệt, làm cho nó nhanh khi hầu hết các dòng được chia sẻ.

1. Xây dựng bảng DP
Tạo ma trận (m+1) x (n+1). Đối với mỗi cặp dòng, lưu trữ độ dài của dãy con chung dài nhất đã thấy cho đến nay. Các dòng bằng nhau mở rộng giá trị đường chéo lên 1.
2. Truy vết ngược
Đi từ góc dưới bên phải của ma trận về (0,0). Các bước di chuyển theo đường chéo trên các dòng bằng nhau tạo ra các mục "không đổi". Các bước di chuyển ngang hoặc dọc tạo ra các mục "đã thêm" hoặc "đã xóa".
3. Hiển thị đầu ra
Ánh xạ mỗi mục thành một dòng hiển thị: các dòng không đổi không có tiền tố, các dòng thêm có tiền tố +, các dòng xóa có tiền tố -. Áp dụng tô màu để rõ ràng hơn về mặt trực quan.

Ví dụ Code

Cài đặt so sánh văn bản từng dòng trong JavaScript, Python, Go và dòng lệnh. Mỗi ví dụ tạo ra đầu ra theo kiểu unified 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

Câu hỏi thường gặp

Sự khác biệt giữa line diff và character diff là gì?
Line diff so sánh văn bản từng dòng: nếu bất kỳ ký tự nào trên một dòng thay đổi, toàn bộ dòng đó được đánh dấu là đã xóa và phiên bản mới được đánh dấu là đã thêm. Character diff (hoặc word diff) hoạt động ở mức độ chi tiết hơn, đánh dấu các ký tự hoặc từ cụ thể đã thay đổi trong một dòng. Line diff là tiêu chuẩn cho code review; character diff hữu ích hơn cho việc chỉnh sửa văn xuôi.
Git diff hoạt động nội bộ như thế nào?
Git sử dụng thuật toán Myers' diff theo mặc định, thuật toán này tìm script chỉnh sửa ngắn nhất (số lượng thao tác chèn và xóa tối thiểu) giữa hai tệp. Git lưu kết quả ở định dạng unified diff với các tiêu đề hunk @@ chỉ định số dòng. Khi bạn chạy git diff, Git so sánh cây làm việc với index (khu vực staging), không phải commit cuối cùng, trừ khi bạn truyền HEAD tường minh.
Tôi có thể so sánh các tệp nhị phân bằng công cụ text diff không?
Không. Các công cụ text diff tách đầu vào theo ký tự xuống dòng và so sánh các chuỗi. Các tệp nhị phân chứa các chuỗi byte tùy ý tạo ra các phần tách dòng vô nghĩa. Để so sánh nhị phân, hãy sử dụng công cụ hex diff hoặc so sánh checksum cấp tệp (sha256sum trên cả hai tệp).
Dữ liệu của tôi có được gửi đến máy chủ khi sử dụng công cụ này không?
Không. Công cụ này chạy hoàn toàn trong trình duyệt của bạn. Việc tính toán diff sử dụng cài đặt LCS bằng JavaScript thực thi phía máy khách. Không có yêu cầu mạng nào được thực hiện với nội dung văn bản của bạn. Bạn có thể xác nhận điều này bằng cách mở tab Mạng trong trình duyệt trong khi sử dụng công cụ.
Định dạng unified diff là gì?
Unified diff là định dạng đầu ra được sử dụng bởi diff -u và git diff. Nó hiển thị các dòng đã thay đổi với tiền tố - (đã xóa) và + (đã thêm), đứng trước các tiêu đề @@ chỉ định phạm vi số dòng trong cả hai tệp. Các dòng ngữ cảnh (không đổi) có tiền tố là khoảng trắng. Unified diff là định dạng phổ biến nhất cho các patch, pull request và công cụ code review.
Làm thế nào để so sánh các tệp lớn có nhiều sự khác biệt?
Đối với các tệp có hàng nghìn dòng, công cụ trên trình duyệt vẫn hoạt động nhưng có thể chậm vì thuật toán LCS tăng theo bậc hai. Đối với các diff rất lớn, các công cụ dòng lệnh như diff hoặc git diff nhanh hơn vì chúng sử dụng cài đặt C được tối ưu hóa và có thể phát trực tuyến đầu ra. Bạn cũng có thể ẩn các dòng không đổi (tắt "Hiển thị dòng không đổi" trong công cụ này) để chỉ tập trung vào các thay đổi.
Three-way merge diff là gì?
Three-way merge so sánh hai phiên bản đã sửa đổi của một tệp với tổ tiên chung của chúng (base). Git sử dụng điều này trong quá trình git merge và git rebase. Nó xác định các thay đổi được thực hiện trong mỗi nhánh so với base và kết hợp chúng lại. Khi cả hai nhánh sửa đổi cùng một dòng, Git báo cáo xung đột merge. Three-way merge yêu cầu ba đầu vào, trong khi một diff tiêu chuẩn chỉ cần hai.