Comparatore di Testi
Confronta due testi affiancati e evidenzia le differenze riga per riga
Testo A
Testo B
Cos'è il Text Diff?
Un text diff (abbreviazione di "difference", differenza) è il risultato del confronto di due blocchi di testo che identifica quali righe sono state aggiunte, rimosse o lasciate invariate. Il concetto nasce dall'utility Unix diff, rilasciata per la prima volta nel 1974 come parte di Version 5 Unix. Oggi il text diff è la base dei sistemi di controllo versione come Git, dove ogni commit memorizza un diff anziché una copia completa di ogni file.
Un algoritmo diff trova la Longest Common Subsequence (LCS) tra due sequenze di righe. Le righe presenti nell'LCS sono marcate come invariate. Le righe del testo originale non presenti nell'LCS sono marcate come rimosse. Le righe del testo modificato non presenti nell'LCS sono marcate come aggiunte. Il risultato è un insieme minimo di modifiche necessarie per trasformare un testo nell'altro.
L'output di un diff può avere diversi formati. Il unified diff (predefinito per git diff) prefissa le righe rimosse con un segno meno e quelle aggiunte con un segno più. Il diff affiancato dispone entrambi i testi in colonne parallele. Questo strumento usa il confronto riga per riga con output a colori: verde per le aggiunte, rosso per le rimozioni e neutro per le righe invariate. Le righe invariate sono mostrate senza prefisso per impostazione predefinita, ma possono essere nascoste per concentrarsi solo sulle modifiche.
Perché usare uno strumento di diff online?
Confrontare testi in un terminale richiede di installare utility diff e gestire le opzioni della riga di comando. Uno strumento diff basato su browser elimina completamente questa complessità.
Casi d'uso del Text Diff
Formati di output diff a confronto
Gli strumenti diff producono output in diversi formati. La tabella seguente riassume i più comuni, cosa li genera e quando ciascuno è utile.
| Formato | Strumento / Sorgente | Descrizione |
|---|---|---|
| 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 |
Come funziona il diff per righe: l'algoritmo LCS
La maggior parte degli strumenti di diff per righe, incluso questo, usa l'algoritmo Longest Common Subsequence (LCS). LCS trova il maggior insieme di righe che compaiono in entrambi i testi nello stesso ordine relativo, senza richiedere che siano contigue. Le righe non presenti nell'LCS sono le differenze effettive.
L'algoritmo LCS standard usa la programmazione dinamica e ha complessità O(m x n), dove m e n sono il numero di righe dei due testi. Per file di grandi dimensioni, varianti ottimizzate come l'algoritmo diff di Myers (usato da Git) riducono questo a O(n + d^2) dove d è il numero di differenze, rendendolo veloce quando la maggior parte delle righe è condivisa.
Esempi di codice
Implementazioni del confronto testuale riga per riga in JavaScript, Python, Go e da riga di comando. Ogni esempio produce output in stile unified diff.
// 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