Text Diff
Jämför två texter sida vid sida och markera radbaserade skillnader
Text A
Text B
Vad är Text Diff?
En text diff (förkortning av "difference", skillnad) är resultatet av att jämföra två textblock och identifiera vilka rader som lagts till, tagits bort eller lämnats oförändrade. Konceptet härstammar från Unix-verktyget diff, som lanserades 1974 som en del av Version 5 Unix. I dag är text diff ryggraden i versionskontrollsystem som Git, där varje incheckning lagrar en diff snarare än en fullständig kopia av varje fil.
En diff-algoritm hittar den längsta gemensamma delföljden (Longest Common Subsequence, LCS) mellan två sekvenser av rader. Rader som ingår i LCS markeras som oförändrade. Rader i originaltexten som inte ingår i LCS markeras som borttagna. Rader i den modifierade texten som inte ingår i LCS markeras som tillagda. Resultatet är en minimal uppsättning ändringar som behövs för att omvandla en text till den andra.
Diff-utdata finns i flera format. Unified diff (standardformat för git diff) prefixar borttagna rader med ett minustecken och tillagda rader med ett plustecken. Side-by-side diff arrangerar båda texterna i parallella kolumner. Det här verktyget använder radbaserad jämförelse med färgkodad utdata: grönt för tillägg, rött för borttagningar och neutralt för oförändrade rader. Oförändrade rader visas utan prefix som standard men kan döljas för att fokusera enbart på vad som ändrats.
Varför använda ett textjämförelseverktyg online?
Att jämföra text i en terminal kräver installation av diff-verktyg och hantering av kommandoradsflaggor. Ett webbläsarbaserat diff-verktyg tar bort all den friktionen.
Användningsfall för Text Diff
Diff-utdataformat jämförda
Diff-verktyg producerar utdata i flera format. Tabellen nedan sammanfattar de vanligaste, vad som genererar dem och när varje format är användbart.
| Format | Verktyg / Källa | Beskrivning |
|---|---|---|
| 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 |
Hur radbaserad diff fungerar: LCS-algoritmen
De flesta radbaserade diff-verktyg, inklusive det här, använder algoritmen Longest Common Subsequence (LCS). LCS hittar den största uppsättningen rader som förekommer i båda texterna i samma relativa ordning, utan att kräva att de är sammanhängande. Raderna som inte ingår i LCS är de faktiska skillnaderna.
Standard-LCS-algoritmen använder dynamisk programmering och körs i O(m x n) tid, där m och n är radantalet i de två texterna. För stora filer reducerar optimerade varianter som Myers' diff-algoritm (används av Git) detta till O(n + d^2) där d är antalet skillnader, vilket gör den snabb när de flesta rader är gemensamma.
Kodexempel
Implementationer av radbaserad textjämförelse i JavaScript, Python, Go och på kommandoraden. Varje exempel producerar unified-stilad diff-utdata.
// 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