Diff: Die allgegenwärtige Differenzberechnung
Von der Git-Versionsverwaltung bis zur Code-Überprüfung, von Konfigurationsänderungen bis zur Datensynchronisation — Diff ist eine Grundoperation in der Softwareentwicklung.
| Szenario |
Diff-Typ |
Werkzeug |
| Codeänderungen |
Zeilen-Diff |
Git |
| Konfigurationsvergleich |
Schlüsselwert-Diff |
Online-Vergleichstool |
| JSON-Daten |
Strukturiertes Diff |
JSON-Diff |
| Rechtliche Dokumente |
Semantisches Diff |
Spezialisiertes Tool |
| Übersetzungsprüfung |
Zeichen-Diff |
Online-Vergleich |
I. Prinzipien des Myers-Diff-Algorithmus
Kerngedanke
Der Myers-Algorithmus sucht das kürzeste Bearbeitungsskript (SES), das den Quelltext in den Zieltext umwandelt — die wenigsten Einfüge-/Löschoperationen.
Quelle: ABCABBA
Ziel: CBABAC
Bearbeitungsskript:
- A (A löschen)
B (B beibehalten)
- C (C löschen)
C (C beibehalten → C vorher einfügen)
A (A beibehalten)
B (B beibehalten)
- B (B löschen)
A (A beibehalten)
+ C (C einfügen)
Algorithmus-Diagramm
0 1 2 3 4 5 6 7
┌───┬───┬───┬───┬───┬───┬───┬───┐
0 │ → │ ↘ │ │ │ │ │ │ │ A
├───┼───┼───┼───┼───┼───┼───┼───┤
1 │ ↓ │ │ ↘ │ │ → │ ↘ │ │ │ B
├───┼───┼───┼───┼───┼───┼───┼───┤
2 │ │ ↓ │ │ ↘ │ ↓ │ │ ↘ │ │ C
├───┼───┼───┼───┼───┼───┼───┼───┤
3 │ │ │ ↓ │ │ │ ↓ │ │ ↘ │ A
├───┼───┼───┼───┼───┼───┼───┼───┤
4 │ │ │ │ ↓ │ ↘ │ │ ↓ │ │ B
├───┼───┼───┼───┼───┼───┼───┼───┤
5 │ │ │ │ │ ↓ │ │ │ ↓ │ B
├───┼───┼───┼───┼───┼───┼───┼───┤
6 │ │ │ │ │ │ ↓ │ ↘ │ │ A
└───┴───┴───┴───┴───┴───┴───┴───┘
C B A B A C B A
→ = rechts (löschen) ↓ = unten (einfügen) ↘ = diagonal (beibehalten)
Zeitkomplexität
| Fall |
Komplexität |
Beschreibung |
| Optimal |
O(ND) |
N=Textlänge, D=Anzahl der Unterschiede |
| Schlechtester |
O((N+M)D) |
Völlig verschiedene Texte |
| Typisch |
O(N+D²) |
Wenige Unterschiede |
II. Verwendung des Textvergleichstools von Tools Ku
Bedienungsschritte
- Öffnen Sie das Textvergleichstool
- Fügen Sie den Originaltext links ein
- Fügen Sie den geänderten Text rechts ein
- Sehen Sie sich die Diff-Ergebnisse an:
- Rot: gelöschte Zeilen
- Grün: hinzugefügte Zeilen
- Gelb: geänderte Zeilen
- Unterstützt zeichenweise Diff-Hervorhebung, präzise bis zu jeder Zeichenänderung
Vergleichsmodi
| Modus |
Granularität |
Anwendungsfall |
| Zeilenweise |
Vergleich pro Zeile |
Codeänderungen |
| Wortweise |
Vergleich pro Wort |
Dokumentrevisionen |
| Zeichenweise |
Vergleich pro Zeichen |
Präzise Korrektur |
III. Spezielle JSON-Diff-Techniken
- Öffnen Sie das JSON-Diff-Tool
- Fügen Sie zwei JSON-Daten ein
- Sehen Sie sich die strukturierten Unterschiede an:
- Hinzugefügte/gelöschte/geänderte Schlüssel
- Wertänderungen (einschließlich Typänderungen)
- Verschachtelte Pfade
JSON-Diff vs. Text-Diff
// Original
{ "name": "张三", "age": 25, "tags": ["a", "b"] }
// Geändert
{ "name": "张三", "age": 26, "tags": ["a", "c"], "email": "z@test.com" }
| Vergleichsmethode |
Ergebnis |
| Text-Diff |
Zeigt Unterschiede zeilenweise, schwer verständlich |
| JSON-Diff |
age: 25→26, tags[1]: "b"→"c", +email: "z@test.com" |
--- a/config.json
+++ b/config.json
@@ -1,5 +1,6 @@
{
"name": "my-project",
- "version": "1.0.0",
+ "version": "2.0.0",
"description": "A tool",
- "private": true
+ "private": true,
+ "license": "MIT"
}
| Symbol |
Bedeutung |
--- |
Originaldatei |
+++ |
Geänderte Datei |
@@ -a,b +c,d @@ |
Diff-Position (Original ab Zeile a, b Zeilen; Geändert ab Zeile c, d Zeilen) |
- |
Gelöschte Zeile |
+ |
Hinzugefügte Zeile |
(Leerzeichen) |
Kontextzeile |
V. Patch-Generierung und -Anwendung
Patch generieren
import * as Diff from 'diff';
const oldText = 'Hello World\nGoodbye World';
const newText = 'Hello World\nGoodbye Cruel World';
// Patch generieren
const patch = Diff.createPatch('file.txt', oldText, newText, 'old', 'new');
console.log(patch);
Ausgabe:
--- old/file.txt
+++ new/file.txt
@@ -1,2 +1,2 @@
Hello World
-Goodbye World
+Goodbye Cruel World
Patch anwenden
// Vorwärts anwenden
const result = Diff.applyPatch(oldText, patch);
// Rückwärts anwenden (Rückgängigmachung)
const reversed = Diff.applyPatch(newText, Diff.reversePatch(patch));
Drei-Wege-Zusammenführung
const base = 'Originaltext';
const branchA = 'Änderung von A';
const branchB = 'Änderung von B';
const merged = Diff.mergeDiff3(base, branchA, branchB);
// Automatische Zusammenführung, Konflikte werden mit <<<<<<< / ======= / >>>>>>> markiert
VI. Praktische Anwendungsszenarien
Erkennung von Konfigurationsdateiänderungen
const oldConfig = JSON.parse(fs.readFileSync('config.old.json', 'utf-8'));
const newConfig = JSON.parse(fs.readFileSync('config.new.json', 'utf-8'));
const diff = Diff.diffJson(oldConfig, newConfig);
diff.forEach(part => {
if (part.added) console.log('Hinzugefügt:', JSON.stringify(part.value, null, 2));
if (part.removed) console.log('Gelöscht:', JSON.stringify(part.value, null, 2));
});
Übersetzungsdatei-Überprüfung
// Konsistenz der chinesisch-englischen Übersetzungsschlüssel vergleichen
const zhKeys = Object.keys(flatten(zhTranslation));
const enKeys = Object.keys(flatten(enTranslation));
const missingInEn = zhKeys.filter(k => !enKeys.includes(k));
const missingInZh = enKeys.filter(k => !zhKeys.includes(k));
API-Antwort-Regressionstests
const expected = await fs.readFile('fixtures/expected.json', 'utf-8');
const actual = await fetch('/api/data').then(r => r.json());
const diff = Diff.diffJson(JSON.parse(expected), actual);
if (diff.some(d => d.added || d.removed)) {
throw new Error('API-Antwort entspricht nicht der Erwartung');
}
VII. Leistungsoptimierung
| Optimierungsstrategie |
Wirkung |
Anwendbar auf |
| Kontextzeilen begrenzen |
Reduziert Ausgabegröße |
Große Dateien |
| Hash-Vorverarbeitung |
Überspringt identische Zeilen |
Sehr große Dateien |
| Blockweiser Vergleich |
Parallele Verarbeitung |
Sehr lange Texte |
| Zeilenebene vor Zeichenebene |
Reduziert Rechenaufwand |
Codedateien |
// Kontextzeilen begrenzen
const patch = Diff.createPatch('file', old, new, '', '', { context: 3 });