Text Diff
Vergelijk twee teksten naast elkaar en markeer verschillen regel voor regel
Tekst A
Tekst B
Wat is Text Diff?
Een text diff (afkorting van "difference") is het resultaat van het vergelijken van twee tekstblokken waarbij wordt vastgesteld welke regels zijn toegevoegd, verwijderd of ongewijzigd. Het concept is afkomstig van het Unix-hulpprogramma diff, voor het eerst uitgebracht in 1974 als onderdeel van Version 5 Unix. Tegenwoordig is text diff de ruggengraat van versiebeheersystemen zoals Git, waarbij elke commit een diff opslaat in plaats van een volledige kopie van elk bestand.
Een diff-algoritme zoekt de Longest Common Subsequence (LCS) tussen twee reeksen regels. Regels die in de LCS aanwezig zijn, worden als ongewijzigd gemarkeerd. Regels in de originele tekst maar niet in de LCS worden als verwijderd gemarkeerd. Regels in de gewijzigde tekst maar niet in de LCS worden als toegevoegd gemarkeerd. Het resultaat is een minimale set wijzigingen die nodig zijn om de ene tekst in de andere te transformeren.
Diff-uitvoer komt in verschillende formaten voor. Unified diff (de standaard voor git diff) geeft verwijderde regels een minteken als prefix en toegevoegde regels een plusteken. Side-by-side diff rangschikt beide teksten in parallelle kolommen. Dit hulpmiddel gebruikt regel-voor-regel vergelijking met kleurgecodeerde uitvoer: groen voor toevoegingen, rood voor verwijderingen en neutraal voor ongewijzigde regels. Ongewijzigde regels worden standaard zonder prefix weergegeven, maar kunnen worden verborgen om alleen de wijzigingen te bekijken.
Waarom een online text diff-tool gebruiken?
Tekst vergelijken in een terminal vereist het installeren van diff-hulpprogramma's en het omgaan met opdrachtregelopties. Een browsergebaseerde diff-tool neemt die drempel volledig weg.
Toepassingen van Text Diff
Diff-uitvoerformaten vergeleken
Diff-hulpmiddelen produceren uitvoer in verschillende formaten. De onderstaande tabel geeft een overzicht van de meest voorkomende, wat ze genereert en wanneer elk nuttig is.
| Formaat | Hulpmiddel / Bron | Omschrijving |
|---|---|---|
| Unified diff | diff -u / git diff | Prefixes lines with + / - / space; includes @@ hunk headers |
| Side-by-side | diff -y / sdiff | Two columns, changed lines aligned horizontally |
| Context diff | diff -c | Shows changed lines with surrounding context, marked with ! / + / - |
| HTML diff | Python difflib | Color-coded HTML table with inline change highlights |
| JSON Patch | RFC 6902 | Array of add/remove/replace operations on a JSON document |
Hoe regel-diff werkt: het LCS-algoritme
De meeste regel-diff-hulpmiddelen, waaronder dit, gebruiken het Longest Common Subsequence (LCS)-algoritme. LCS vindt de grootste reeks regels die in beide teksten in dezelfde relatieve volgorde voorkomt, zonder dat ze aaneengesloten hoeven te zijn. De regels die niet in de LCS zitten, zijn de feitelijke verschillen.
Het standaard LCS-algoritme gebruikt dynamisch programmeren en heeft een tijdcomplexiteit van O(m x n), waarbij m en n het aantal regels in de twee teksten zijn. Voor grote bestanden verminderen geoptimaliseerde varianten zoals het Myers' diff-algoritme (gebruikt door Git) dit tot O(n + d^2) waarbij d het aantal verschillen is — waardoor het snel blijft wanneer de meeste regels gedeeld worden.
Codevoorbeelden
Implementaties van regel-voor-regel tekstvergelijking in JavaScript, Python, Go en de opdrachtregel. Elk voorbeeld produceert uitvoer in unified-diff-stijl.
// 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' }
// ]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')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# 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