Diff チェッカーは、すべての開発者、ライター、システム管理者のツールキットに不可欠なツールです。2つのファイルを比較したり、コード変更をレビューしたり、2つのテキスト間の違いを見つけたりする必要がある場合、diffアルゴリズムの仕組みを理解することで生産性が大幅に向上します。この包括的なガイドでは、テキストdiffアルゴリズムの基礎からGit、JavaScript、Python、Bashの実用的なコード例まですべてをカバーします。
無料オンラインテキストDiffチェッカーツールをお試しください。
Diff チェッカーとは?
Diff チェッカー(テキスト比較ツールとも呼ばれる)は、2つのテキストまたはファイルを入力として受け取り、それらの間の違いを特定するソフトウェアです。出力は追加、削除、変更された行を示します。このコンセプトは1974年にDouglas McIlroyが作成したUnixのdiffコマンドに由来します。
Diffチェッカーは「バージョンAとバージョンBの間で何が変わったか?」という問題を効率的に解決します。diffアルゴリズムはテキストAをテキストBに変換する最小の編集セットを見つける必要があり、LCS(最長共通部分列)アルゴリズムに基づいています。
最新のオンラインdiffツールは、シンタックスハイライト、文字レベルの差分検出、サイドバイサイドおよびインラインビュー、空白無視オプション、セマンティックdiff機能を提供します。
Diffアルゴリズムの仕組み
すべてのdiffチェッカーの基盤はLCS(最長共通部分列)アルゴリズムです。2つの入力で同じ順序で現れる最長の要素列を見つけます。
古典的なLCSアルゴリズムの計算量はO(n*m)です。Myersアルゴリズム(1986年)は差異数dに対してO(n*d)で最短編集スクリプトを見つけます。これがGitで使用されているアルゴリズムです。
Myersアルゴリズムは編集操作のグラフを探索し、両テキストの最初から最後までの最短パスを最適に見つけます。
行単位vs文字単位のdiff:ほとんどのツールはまず行レベルで比較し、変更された行内で文字レベルのdiffを実行して読みやすい出力を生成します。
Diff出力の種類
diff結果の表示にはいくつかの標準フォーマットがあります:
Unified Diff:git diffで使用される最も一般的なフォーマット。+と-プレフィックスと@@マーカーで変更を表示します。
サイドバイサイドDiff:元のテキストと変更後のテキストを2列に並べて表示し、変更をハイライトします。
インラインDiff:古いバージョンと新しいバージョンを1列に交互に表示し、色分け(赤/緑)します。
コンテキストDiff:!で変更行をマークし、***と---セクションを持つ古いフォーマット。
ワードDiff:行全体ではなく個々の単語を比較します。Gitはgit diff --word-diffでサポートしています。
一般的なユースケース
Diff チェックとテキスト比較は多くのプロフェッショナルなワークフローに不可欠です:
コードレビュー:すべてのプルリクエストは基本的にdiffです。開発者は変更を理解しコード品質を確認するためにdiff出力をレビューします。
ドキュメントバージョニング:ライターや法律専門家はリビジョン間の変更を追跡するためにファイルを頻繁に比較します。
設定ファイルの変更:管理者は変更前後の設定ファイルを比較して、意図した変更のみが行われたことを確認します。
APIレスポンス比較:開発者は環境間のJSON/XMLレスポンスを比較して予期しない変更を特定します。
マージコンフリクト解決:diff出力の理解はGitコンフリクトを正しく解決するために不可欠です。
Diffコード例
Git Diffコマンド
Gitには最も強力な組み込みdiffチェッカーがあります。必須のgit diffコマンドをご紹介します:
# ===== Basic git diff commands =====
# Compare working directory with staging area (unstaged changes)
git diff
# Compare staging area with last commit (staged changes)
git diff --staged
# or equivalently:
git diff --cached
# Compare working directory with last commit (all changes)
git diff HEAD
# Compare two specific commits
git diff abc1234 def5678
# Compare current branch with another branch
git diff main..feature-branch
# Compare a specific file between commits
git diff HEAD~3 HEAD -- src/app.js
# ===== Advanced git diff options =====
# Word-level diff (great for prose and documentation)
git diff --word-diff
# Output: [-old word-]{+new word+}
# Word diff with color only (no markers)
git diff --word-diff=color
# Show only file names that changed
git diff --name-only HEAD~5
# Show file names with change status (Added/Modified/Deleted)
git diff --name-status main..feature
# Show diff statistics (insertions/deletions per file)
git diff --stat
# Output:
# src/app.js | 15 +++++++++------
# src/utils.js | 8 +++++---
# 2 files changed, 14 insertions(+), 9 deletions(-)
# One-line summary of changes
git diff --shortstat
# Output: 2 files changed, 14 insertions(+), 9 deletions(-)
# Ignore whitespace changes
git diff -w
# or: git diff --ignore-all-space
# Ignore blank line changes
git diff --ignore-blank-lines
# Show diff with 10 lines of context (default is 3)
git diff -U10
# Generate a patch file
git diff > my-changes.patch
# Apply a patch file
git apply my-changes.patch
# Check if a patch applies cleanly (dry run)
git apply --check my-changes.patchJavaScript テキストDiff(diff npmパッケージ)
diff npmパッケージはテキストdiff計算のための最も人気のあるJavaScriptライブラリです:
// npm install diff
const Diff = require('diff');
const oldText = `function greet(name) {
console.log("Hello, " + name);
return true;
}`;
const newText = `function greet(name, greeting) {
console.log(greeting + ", " + name + "!");
return true;
}`;
// ===== Line-by-line diff =====
const lineDiff = Diff.diffLines(oldText, newText);
lineDiff.forEach(part => {
const prefix = part.added ? '+' : part.removed ? '-' : ' ';
const lines = part.value.split('\n').filter(l => l);
lines.forEach(line => console.log(prefix + ' ' + line));
});
// Output:
// - function greet(name) {
// + function greet(name, greeting) {
// - console.log("Hello, " + name);
// + console.log(greeting + ", " + name + "!");
// return true;
// }
// ===== Character-level diff =====
const charDiff = Diff.diffChars('hello world', 'hello there');
charDiff.forEach(part => {
const color = part.added ? '\x1b[32m' : part.removed ? '\x1b[31m' : '';
process.stdout.write(color + part.value + '\x1b[0m');
});
// Highlights exact character changes
// ===== Word-level diff =====
const wordDiff = Diff.diffWords(
'The quick brown fox jumps over the lazy dog',
'The slow brown fox leaps over the tired dog'
);
wordDiff.forEach(part => {
if (part.added) console.log('[+] ' + part.value);
else if (part.removed) console.log('[-] ' + part.value);
});
// ===== Generate unified patch =====
const patch = Diff.createPatch(
'greeting.js', // filename
oldText, // old content
newText, // new content
'original', // old header
'modified' // new header
);
console.log(patch);
// Output: standard unified diff format
// ===== Apply a patch =====
const applied = Diff.applyPatch(oldText, patch);
console.log(applied === newText); // true
// ===== Structured patch for multiple files =====
const structuredPatch = Diff.structuredPatch(
'old/file.js', 'new/file.js',
oldText, newText, '', ''
);
console.log(JSON.stringify(structuredPatch.hunks, null, 2));Python Diff(difflibモジュール)
Pythonは標準ライブラリに強力なdifflibモジュールを含んでいます:
import difflib
old_text = """function greet(name) {
console.log("Hello, " + name);
return true;
}""".splitlines(keepends=True)
new_text = """function greet(name, greeting) {
console.log(greeting + ", " + name + "!");
return true;
}""".splitlines(keepends=True)
# ===== Unified diff (most common format) =====
diff = difflib.unified_diff(
old_text, new_text,
fromfile='greeting.js.orig',
tofile='greeting.js',
lineterm=''
)
print('\n'.join(diff))
# Output:
# --- greeting.js.orig
# +++ greeting.js
# @@ -1,4 +1,4 @@
# -function greet(name) {
# - console.log("Hello, " + name);
# +function greet(name, greeting) {
# + console.log(greeting + ", " + name + "!");
# return true;
# }
# ===== Context diff (older format) =====
ctx_diff = difflib.context_diff(
old_text, new_text,
fromfile='original', tofile='modified'
)
print('\n'.join(ctx_diff))
# ===== HTML visual diff report =====
d = difflib.HtmlDiff()
html = d.make_file(
old_text, new_text,
fromdesc='Original',
todesc='Modified',
context=True, # show only changed sections
numlines=3 # lines of context
)
with open('diff_report.html', 'w') as f:
f.write(html)
# ===== SequenceMatcher for similarity ratio =====
seq = difflib.SequenceMatcher(None,
''.join(old_text), ''.join(new_text))
print(f"Similarity ratio: {seq.ratio():.2%}")
# Output: Similarity ratio: 72.41%
# ===== Get matching blocks =====
for block in seq.get_matching_blocks():
print(f" a[{block.a}:{block.a+block.size}] == "
f"b[{block.b}:{block.b+block.size}] "
f"(size={block.size})")
# ===== Get opcodes (edit operations) =====
for op, i1, i2, j1, j2 in seq.get_opcodes():
print(f" {op:8s} a[{i1}:{i2}] b[{j1}:{j2}]")
# Output:
# equal a[0:0] b[0:0]
# replace a[0:28] b[0:38]
# equal a[28:50] b[38:60]
# ===== Compare two files =====
with open('file1.txt') as f1, open('file2.txt') as f2:
diff = difflib.unified_diff(
f1.readlines(), f2.readlines(),
fromfile='file1.txt', tofile='file2.txt'
)
print('\n'.join(diff))Bash / Linux Diffコマンド
LinuxとmacOSはテキスト比較のための複数のコマンドラインツールを提供します:
# ===== Basic diff command =====
# Compare two files (default output format)
diff file1.txt file2.txt
# Unified diff format (most readable)
diff -u file1.txt file2.txt
# Output:
# --- file1.txt 2024-01-15 10:30:00
# +++ file2.txt 2024-01-15 11:45:00
# @@ -1,4 +1,4 @@
# -old line 1
# +new line 1
# unchanged line
# Side-by-side comparison
diff -y file1.txt file2.txt
# or with specific width:
diff -y -W 120 file1.txt file2.txt
# Show only lines that differ (with side-by-side)
diff -y --suppress-common-lines file1.txt file2.txt
# Ignore case differences
diff -i file1.txt file2.txt
# Ignore all whitespace
diff -w file1.txt file2.txt
# Ignore blank lines
diff -B file1.txt file2.txt
# Recursive directory comparison
diff -r dir1/ dir2/
# Brief output (just report if files differ)
diff -q file1.txt file2.txt
# Output: Files file1.txt and file2.txt differ
# ===== Enhanced diff tools =====
# colordiff: colorized diff output
# Install: apt install colordiff / brew install colordiff
colordiff -u file1.txt file2.txt
# vimdiff: side-by-side in Vim editor
vimdiff file1.txt file2.txt
# sdiff: interactive side-by-side merge
sdiff file1.txt file2.txt
# ===== Practical examples =====
# Compare command output
diff <(ls dir1/) <(ls dir2/)
# Compare sorted files
diff <(sort file1.txt) <(sort file2.txt)
# Compare remote file with local file
diff <(curl -s https://example.com/config.yml) local-config.yml
# Generate a patch file
diff -u original.txt modified.txt > changes.patch
# Apply a patch
patch original.txt < changes.patch
# Dry run (check if patch applies cleanly)
patch --dry-run original.txt < changes.patch
# Reverse a patch
patch -R original.txt < changes.patch
# Compare two strings directly
diff <(echo "hello world") <(echo "hello there")Diff出力の読み方
diff出力の理解は基本的な開発者スキルです。Unified Diffフォーマットを解説します:
ファイルヘッダー:diffは--- a/file.txt(元のファイル)と+++ b/file.txt(変更後のファイル)で始まります。
ハンクヘッダー:@@行は@@ -開始,行数 +開始,行数 @@のフォーマットで変更の位置を示します。
変更行:-行(赤)は削除、+行(緑)は追加。プレフィックスなしの行はコンテキストです。
# Example: reading a unified diff
diff --git a/src/config.js b/src/config.js
index 8a3b5c1..f29d4e2 100644
--- a/src/config.js ← original file
+++ b/src/config.js ← modified file
@@ -12,8 +12,9 @@ const defaults = { ← hunk header: line 12, 8→9 lines
timeout: 3000, ← context (unchanged)
retries: 3, ← context (unchanged)
- baseUrl: 'http://localhost:3000', ← REMOVED (red)
- debug: false, ← REMOVED (red)
+ baseUrl: 'https://api.example.com', ← ADDED (green)
+ debug: true, ← ADDED (green)
+ verbose: true, ← ADDED (green, new line)
headers: { ← context (unchanged)
'Content-Type': 'application/json' ← context (unchanged)
} ← context (unchanged)パッチファイル:diffは.patchファイルとして保存し、git applyやpatch -p1で適用できます。
統計:git diff --statでファイルごとの変更概要を表示できます。
# git diff --stat output example:
src/config.js | 5 +++--
src/app.js | 12 ++++++------
tests/config.test | 28 ++++++++++++++++++++++++++++
3 files changed, 37 insertions(+), 8 deletions(-)
# The bar shows the ratio of additions (+) to deletions (-)
# Longer bars = more changes in that file最適なDiffツール比較
利用可能な最適なdiffチェッカーツールの比較です:
| Tool | Type | Price | Platform | Best For |
|---|---|---|---|---|
| DevToolBox | Web | Free | Any browser | Quick online comparisons |
| VS Code | Editor | Free | Win/Mac/Linux | Code review + editing |
| Beyond Compare | Desktop | $35-60 | Win/Mac/Linux | 3-way merge, folder sync |
| Meld | Desktop | Free | Linux/Mac/Win | Visual diff + VCS integration |
| WinMerge | Desktop | Free | Windows | File + folder comparison |
| diff (CLI) | CLI | Free | Unix/Linux/Mac | Scripting + CI/CD pipelines |
| git diff | CLI | Free | Any | Version control diffs |
DevToolBox テキストDiff(オンライン):無料のオンラインdiffツールで、シンタックスハイライト付きの即時サイドバイサイド比較を提供します。
VS Code内蔵Diff:VS Codeにはインラインとサイドバイサイドビューを備えた優れたdiffエディタが含まれています。
Beyond Compare:3方向マージとフォルダ同期を備えた商用diffツール。
Meld:Linux、macOS、Windows向けの無料オープンソースビジュアルdiffツール。
WinMerge:シェル統合を備えたWindows向け無料オープンソースdiffツール。
diffコマンド(Unix):高速でスクリプト可能なオリジナルのコマンドラインdiffツール。
よくある質問
オンラインで2つのテキストファイルを比較するには?
無料のText Diff Checkerのようなオンラインdiffツールに各ファイルの内容を貼り付けてください。ツールは追加行(緑)、削除行(赤)、変更なしのコンテキスト行を即座にハイライトします。ほとんどのオンラインツールはサイドバイサイドとインラインビューをサポートしています。
diff出力の + と - は何を意味しますか?
Unified Diffフォーマットで、+で始まる行は追加されたコンテンツ、-で始まる行は削除されたコンテンツを示します。プレフィックスなしの行は変更のないコンテキストです。@@マーカーは変更が発生する行番号を示します。
git diffはどのように動作しますか?
Git diffはMyersアルゴリズムを使用して2つのバージョン間の最小の変更セットを見つけます。引数なしの場合、作業ディレクトリとインデックスを比較します。git diff --stagedはインデックスとHEADを比較します。出力はUnified Diffフォーマットです。
Diffツールの効果的な使用は、開発者と管理者にとって基本的なスキルです。無料のオンラインツールで即座にテキスト比較を行えます。