Comparateur de Textes
Comparez deux textes côte à côte et mettez en évidence les différences ligne par ligne
Texte A
Texte B
Qu'est-ce que le diff de texte ?
Un diff de texte (abréviation de « difference », différence) est le résultat de la comparaison de deux blocs de texte permettant d'identifier les lignes ajoutées, supprimées ou inchangées. Le concept est issu de l'utilitaire Unix diff, publié pour la première fois en 1974 dans Unix Version 5. Aujourd'hui, le diff de texte est au cœur des systèmes de contrôle de version comme Git, où chaque commit stocke un diff plutôt qu'une copie complète de chaque fichier.
Un algorithme de diff recherche la Plus Longue Sous-Séquence Commune (LCS) entre deux séquences de lignes. Les lignes présentes dans la LCS sont marquées comme inchangées. Les lignes du texte original absentes de la LCS sont marquées comme supprimées. Les lignes du texte modifié absentes de la LCS sont marquées comme ajoutées. Le résultat est un ensemble minimal de changements nécessaires pour transformer un texte en l'autre.
La sortie d'un diff existe sous plusieurs formats. Le format unified diff (format par défaut de git diff) préfixe les lignes supprimées par un signe moins et les lignes ajoutées par un signe plus. Le diff côte à côte dispose les deux textes en colonnes parallèles. Cet outil utilise une comparaison ligne par ligne avec un rendu coloré : vert pour les ajouts, rouge pour les suppressions, et neutre pour les lignes inchangées. Les lignes inchangées sont affichées sans préfixe par défaut, mais peuvent être masquées pour se concentrer uniquement sur les modifications.
Pourquoi utiliser un outil de diff en ligne ?
Comparer des textes dans un terminal nécessite d'installer des utilitaires diff et de gérer des options de ligne de commande. Un outil de diff dans le navigateur supprime complètement cette friction.
Cas d'utilisation du diff de texte
Comparaison des formats de sortie diff
Les outils de diff produisent leur sortie dans plusieurs formats. Le tableau ci-dessous résume les plus courants, ce qui les génère et dans quel cas chacun est utile.
| Format | Outil / Source | Description |
|---|---|---|
| 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 |
Comment fonctionne le diff ligne par ligne : l'algorithme LCS
La plupart des outils de diff ligne par ligne, dont celui-ci, utilisent l'algorithme de la Plus Longue Sous-Séquence Commune (LCS). La LCS recherche le plus grand ensemble de lignes présentes dans les deux textes dans le même ordre relatif, sans qu'elles soient nécessairement contiguës. Les lignes absentes de la LCS constituent les différences effectives.
L'algorithme LCS standard utilise la programmation dynamique et s'exécute en O(m x n), où m et n sont le nombre de lignes des deux textes. Pour les fichiers volumineux, des variantes optimisées comme l'algorithme de Myers (utilisé par Git) ramènent la complexité à O(n + d²), où d est le nombre de différences, ce qui le rend rapide lorsque la majorité des lignes sont communes.
Exemples de code
Implémentations de la comparaison de texte ligne par ligne en JavaScript, Python, Go et en ligne de commande. Chaque exemple produit une sortie au format 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