Text Diff
Vergleiche zwei Texte nebeneinander und markiere Unterschiede zeilenweise
Text A
Text B
Was ist Text Diff?
Ein Text Diff (kurz für „Difference”, Unterschied) ist das Ergebnis des Vergleichs zweier Textblöcke, bei dem ermittelt wird, welche Zeilen hinzugefügt, entfernt oder unverändert geblieben sind. Das Konzept stammt vom Unix-diff-Dienstprogramm, das 1974 erstmals als Teil von Version 5 Unix veröffentlicht wurde. Heute ist Text Diff das Rückgrat von Versionskontrollsystemen wie Git, bei dem jeder Commit einen Diff speichert anstatt eine vollständige Kopie jeder Datei.
Ein Diff-Algorithmus findet die Longest Common Subsequence (LCS) zwischen zwei Zeilenfolgen. Zeilen, die in der LCS vorhanden sind, werden als unverändert markiert. Zeilen im Originaltext, die nicht in der LCS enthalten sind, werden als entfernt markiert. Zeilen im geänderten Text, die nicht in der LCS enthalten sind, werden als hinzugefügt markiert. Das Ergebnis ist ein minimaler Satz von Änderungen, der notwendig ist, um einen Text in den anderen umzuwandeln.
Diff-Ausgaben gibt es in verschiedenen Formaten. Unified diff (Standard für git diff) kennzeichnet entfernte Zeilen mit einem Minuszeichen und hinzugefügte Zeilen mit einem Pluszeichen. Side-by-side diff ordnet beide Texte in parallelen Spalten an. Dieses Tool verwendet einen zeilenweisen Vergleich mit farbcodierter Ausgabe: Grün für Ergänzungen, Rot für Entfernungen und Neutral für unveränderte Zeilen. Unveränderte Zeilen werden standardmäßig ohne Präfix angezeigt, können aber ausgeblendet werden, um sich nur auf das Geänderte zu konzentrieren.
Warum ein Online-Text-Diff-Tool verwenden?
Texte im Terminal zu vergleichen erfordert die Installation von diff-Dienstprogrammen und den Umgang mit Kommandozeilenoptionen. Ein browserbasiertes Diff-Tool beseitigt diese Hürden vollständig.
Anwendungsfälle für Text Diff
Diff-Ausgabeformate im Vergleich
Diff-Tools erzeugen Ausgaben in verschiedenen Formaten. Die folgende Tabelle fasst die gängigsten zusammen, was sie erzeugt und wann jedes Format nützlich ist.
| Format | Tool / Quelle | Beschreibung |
|---|---|---|
| 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 |
Wie Zeilen-Diff funktioniert: Der LCS-Algorithmus
Die meisten Zeilen-Diff-Tools, einschließlich dieses, verwenden den Longest Common Subsequence (LCS) Algorithmus. LCS findet die größte Menge von Zeilen, die in beiden Texten in derselben relativen Reihenfolge vorkommen, ohne dass sie aufeinanderfolgend sein müssen. Die Zeilen, die nicht in der LCS enthalten sind, stellen die tatsächlichen Unterschiede dar.
Der Standard-LCS-Algorithmus verwendet dynamische Programmierung und läuft in O(m x n) Zeit, wobei m und n die Zeilenanzahlen der beiden Texte sind. Bei großen Dateien reduzieren optimierte Varianten wie Myers' Diff-Algorithmus (von Git verwendet) dies auf O(n + d^2), wobei d die Anzahl der Unterschiede ist — was ihn schnell macht, wenn die meisten Zeilen übereinstimmen.
Code-Beispiele
Implementierungen des zeilenweisen Textvergleichs in JavaScript, Python, Go und der Kommandozeile. Jedes Beispiel erzeugt eine Ausgabe im unified-diff-Stil.
// 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