Tools•8 min read
Text Diff and Comparison: Developer Guide
Learn text comparison algorithms, diff formats, and tools. Understand how version control systems track changes.
By Andy Pham
Text Diff and Comparison: Developer Guide
Text diff tools show differences between two versions of text. This guide covers diff algorithms, formats, and practical applications.
Understanding Diff Output
Unified Diff Format
--- original.txt
+++ modified.txt
@@ -1,4 +1,5 @@
Line 1 unchanged
-Line 2 removed
+Line 2 modified
+Line 2.5 added
Line 3 unchanged
Line 4 unchanged
Format explanation:
---Old file header+++New file header@@Line range information(space) Unchanged line-Removed line+Added line
Diff Algorithms
Longest Common Subsequence (LCS)
function lcs(text1, text2) {
const m = text1.length;
const n = text2.length;
const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (text1[i - 1] === text2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
Simple Line-by-Line Diff
function simpleDiff(original, modified) {
const originalLines = original.split('
');
const modifiedLines = modified.split('
');
const result = [];
let i = 0, j = 0;
while (i < originalLines.length || j < modifiedLines.length) {
if (i >= originalLines.length) {
result.push({ type: 'add', line: modifiedLines[j] });
j++;
} else if (j >= modifiedLines.length) {
result.push({ type: 'remove', line: originalLines[i] });
i++;
} else if (originalLines[i] === modifiedLines[j]) {
result.push({ type: 'same', line: originalLines[i] });
i++;
j++;
} else {
result.push({ type: 'remove', line: originalLines[i] });
result.push({ type: 'add', line: modifiedLines[j] });
i++;
j++;
}
}
return result;
}
Character-Level Diff
function charDiff(str1, str2) {
const result = [];
let i = 0, j = 0;
while (i < str1.length && j < str2.length) {
if (str1[i] === str2[j]) {
result.push({ type: 'same', char: str1[i] });
i++;
j++;
} else {
// Find next matching position
const nextInStr2 = str2.indexOf(str1[i], j);
const nextInStr1 = str1.indexOf(str2[j], i);
if (nextInStr2 !== -1 && (nextInStr1 === -1 || nextInStr2 - j < nextInStr1 - i)) {
while (j < nextInStr2) {
result.push({ type: 'add', char: str2[j] });
j++;
}
} else {
result.push({ type: 'remove', char: str1[i] });
i++;
}
}
}
while (i < str1.length) {
result.push({ type: 'remove', char: str1[i++] });
}
while (j < str2.length) {
result.push({ type: 'add', char: str2[j++] });
}
return result;
}
Visual Diff Display
function renderDiff(diff) {
return diff.map(item => {
switch (item.type) {
case 'add':
return `<span class="diff-add">+${item.line}</span>`;
case 'remove':
return `<span class="diff-remove">-${item.line}</span>`;
default:
return `<span class="diff-same"> ${item.line}</span>`;
}
}).join('
');
}
// CSS
const styles = `
.diff-add { background: #e6ffed; color: #22863a; }
.diff-remove { background: #ffeef0; color: #cb2431; }
.diff-same { color: #6a737d; }
`;
Side-by-Side Diff
function sideBySideDiff(original, modified) {
const diff = simpleDiff(original, modified);
const left = [];
const right = [];
for (const item of diff) {
switch (item.type) {
case 'same':
left.push(item.line);
right.push(item.line);
break;
case 'remove':
left.push(item.line);
right.push('');
break;
case 'add':
left.push('');
right.push(item.line);
break;
}
}
return { left, right };
}
Use Cases
1. Code Review
// Show changes between commits
function reviewChanges(oldCode, newCode) {
const diff = simpleDiff(oldCode, newCode);
const additions = diff.filter(d => d.type === 'add').length;
const deletions = diff.filter(d => d.type === 'remove').length;
return {
diff,
summary: `+${additions} -${deletions}`
};
}
2. Document Comparison
function compareDocuments(doc1, doc2) {
// Normalize whitespace
const normalize = text => text.replace(/s+/g, ' ').trim();
const words1 = normalize(doc1).split(' ');
const words2 = normalize(doc2).split(' ');
return simpleDiff(words1.join('
'), words2.join('
'));
}
3. Real-Time Sync
// Track changes in text editor
let lastContent = '';
function trackChanges(currentContent) {
const changes = simpleDiff(lastContent, currentContent);
lastContent = currentContent;
return changes;
}
Best Practices
- Normalize line endings before comparing
- Ignore whitespace for semantic comparison
- Use word-level diff for prose
- Use line-level diff for code
- Show context around changes
function normalizeText(text) {
return text
.replace(/
/g, '
') // Normalize line endings
.replace(/ /g, ' ') // Convert tabs to spaces
.trimEnd(); // Remove trailing whitespace
}
Try Our Tools
- Text Diff - Compare text differences
Conclusion
Text diff tools are essential for version control, code review, and document comparison. Understanding diff algorithms helps you build better comparison features.