Man exportiert eine Tabelle von einer deutschen Website. Die Umsatzspalte zeigt „1.234,56 €".
Man fügt es in Excel ein. Es wird zu „1.23456E" oder, schlimmer noch, zu einem Datum.
Willkommen beim internationalen Zahlenformat-Problem.
Das Kernproblem: Was bedeutet „1.234"?
In den USA und UK: 1.234 = eins Komma zwei drei vier (Dezimal)
In Deutschland, Spanien und den meisten Ländern Lateinamerikas: 1.234 = eintausendzweihundertvierunddreißig
Dieselben Zeichen. Völlig unterschiedliche Werte.
Beim Extrahieren von Daten aus internationalen Websites kann man nicht annehmen, welches Format vorliegt. Und wenn man falsch liegt, ist die Analyse um Größenordnungen daneben.
Die Drei-Stellen-Mehrdeutigkeit
Der schwierigste Fall: exakt drei Stellen nach einem Trennzeichen.
| Eingabe | US-Interpretation | EU-Interpretation |
|---|---|---|
| 1.234 | 1,234 (Dezimal) | 1.234 (Tausender) |
| 1,234 | 1.234 (Tausender) | 1,234 (Dezimal) |
| 1.234.567 | Ungültig | 1.234.567 |
| 1,234,567 | 1.234.567 | Ungültig |
Mit zwei Stellen danach ist es eindeutig Dezimal: „1.23" oder „1,23".
Mit vier+ Stellen danach ist es eindeutig Dezimal: „3.14159".
Mit drei Stellen? Kann beides sein.
Der Erkennungsalgorithmus
Hier ein heuristischer Ansatz, der die meisten Praxisfälle abdeckt:
function normalizeNumberString(value) {
if (typeof value !== "string") return value;
const v = value.trim();
if (!v || !/[0-9]/.test(v)) return value;
// Schritt 1: Währungssymbole, Prozente, Leerzeichen bereinigen
let cleaned = v
.replace(/ /g, " ")
.replace(/^(USD|EUR|GBP|R\$|MXN)\s*/i, "")
.replace(/[$€£¥₹₽₩]/g, "")
.replace(/%/g, "")
.replace(/\s+/g, "");
// Schritt 2: Negative Vorzeichen behandeln
const isNegative = cleaned.match(/^-/);
if (isNegative) {
cleaned = cleaned.substring(1).trim();
}
// Schritt 3: Nur Ziffern? Fertig
if (/^\d+$/.test(cleaned)) {
return isNegative ? `-${cleaned}` : cleaned;
}
// Schritt 4: Trennzeichen erkennen
const hasComma = cleaned.includes(",");
const hasDot = cleaned.includes(".");
// Schritt 5: Heuristik anwenden
let decimalSeparator = ".";
let thousandsSeparator = ",";
if (hasComma && hasDot) {
// Beide vorhanden: das letzte ist das Dezimalzeichen
const lastComma = cleaned.lastIndexOf(",");
const lastDot = cleaned.lastIndexOf(".");
if (lastComma > lastDot) {
decimalSeparator = ",";
thousandsSeparator = ".";
}
} else if (hasComma && !hasDot) {
const parts = cleaned.split(",");
if (parts.length === 2 && parts[1].length <= 2) {
// "1,23" → Dezimal
decimalSeparator = ",";
} else {
// "1,234" oder "1,234,567" → Tausender
decimalSeparator = null;
thousandsSeparator = ",";
}
} else if (hasDot && !hasComma) {
const parts = cleaned.split(".");
if (parts.length === 2) {
const afterDot = parts[1];
if (afterDot.length <= 2 || afterDot.length >= 4) {
// "1.23" oder "3.14159" → Dezimal
decimalSeparator = ".";
} else {
// "1.234" → TAUSENDER (pragmatische Wahl für Tabellen)
decimalSeparator = null;
thousandsSeparator = ".";
}
} else {
// Mehrere Punkte → Tausender
decimalSeparator = null;
thousandsSeparator = ".";
}
}
// Schritt 6: Tausendertrennzeichen entfernen
if (thousandsSeparator) {
cleaned = cleaned.replace(new RegExp(`\\${thousandsSeparator}`, "g"), "");
}
// Schritt 7: Dezimalzeichen zu Punkt normalisieren
if (decimalSeparator && decimalSeparator !== ".") {
cleaned = cleaned.replace(decimalSeparator, ".");
}
// Schritt 8: Validieren
const num = Number(cleaned);
if (!Number.isFinite(num)) return value;
return isNegative ? `-${cleaned}` : cleaned;
}
Warum drei Stellen = Tausender
Die Schlüsselentscheidung: Wenn wir exakt drei Stellen nach einem einzelnen Trennzeichen sehen (wie „1.234"), interpretieren wir es als Tausender, nicht als Dezimal.
Warum? In HTML-Tabellen:
- Finanzdaten mit Tausendern sind extrem häufig: $1.234, €1.234
- Dezimalzahlen mit exakt drei Stellen sind in der Praxis selten
- Wissenschaftliche Daten mit drei Dezimalstellen (wie „3.141") werden meist als „3.14159" oder einfach „3.14" geschrieben
Diese Heuristik ist ein pragmatischer Kompromiss, der für die Mehrheit realer Tabellen funktioniert.
Die vollständige Format-Matrix
| Eingabe | Interpretation | Normalisierte Ausgabe |
|---|---|---|
| 1.234,56 | EU-Dezimal | 1234.56 |
| 1,234.56 | US-Dezimal | 1234.56 |
| 1.234.567,89 | EU-Tausender + Dezimal | 1234567.89 |
| 1,234,567.89 | US-Tausender + Dezimal | 1234567.89 |
| 1.234 | Tausender | 1234 |
| 1,234 | Tausender | 1234 |
| 3.14 | Dezimal | 3.14 |
| 3,14 | Dezimal | 3.14 |
| 3.14159 | Dezimal (4+ Stellen) | 3.14159 |
| $ 1.200,50 | Währung + EU | 1200.50 |
| -5.000 | Negative Tausender | -5000 |
| 12.5% | Prozent | 12.5 |
Währungscodes behandeln
Internationale Daten kommen mit Währungspräfixen und -suffixen. Diese zuerst bereinigen:
// Währungscodes VOR Symbolen entfernen
.replace(/^(USD|EUR|GBP|JPY|CHF|CAD|AUD|CNY|INR|BRL|R\$|MXN|KRW)\s*/i, "")
// Dann Symbole entfernen
.replace(/[$€£¥₹₽₩₪฿₫₴₦]/g, "")
Die Reihenfolge ist wichtig: „R$" (Brasilianischer Real) enthält „$", also verhindert das vorherige Entfernen der Währungscodes partielle Treffer.
Wenn die Heuristik versagt
Keine Heuristik ist perfekt. Man bekommt falsche Ergebnisse bei:
- Wissenschaftliche Daten mit exakt 3 Dezimalstellen: „3.141" wird zu „3141"
- Preise unter 10 € mit 3 Dezimalstellen: „1.234 €" wird zu „1234 €"
- Gemischte Formate in einer Spalte: Manche Zeilen EU, manche US
Für Fall 3 braucht man spaltenweite Formaterkennung:
function detectColumnFormat(values) {
let euIndicators = 0;
let usIndicators = 0;
for (const v of values) {
if (/\d\.\d{3},\d{2}$/.test(v)) euIndicators++;
if (/\d,\d{3}\.\d{2}$/.test(v)) usIndicators++;
}
if (euIndicators > usIndicators) return "eu";
if (usIndicators > euIndicators) return "us";
return "auto"; // Zellweise Heuristik verwenden
}
Export-Profile für verschiedene Regionen
Wenn man das Herkunftsland kennt, kann man die Mehrdeutigkeit komplett vermeiden, indem man regionsspezifische Profile verwendet:
- Europäisches Format: Nimmt Komma als Dezimaltrennzeichen an, verwendet Semikolon als CSV-Trennzeichen
- US/UK-Format: Nimmt Punkt als Dezimaltrennzeichen an, verwendet Komma als CSV-Trennzeichen
HTML Table Exporter enthält vordefinierte Profile für beide Formate, plus spezialisierte Profile für Tools wie Pandas und DuckDB.
Häufige Fallstricke
Präzision nicht verlieren
// Falsch: verliert Präzision bei großen Zahlen
const num = parseFloat("12345678901234567890");
// 12345678901234567000 (JavaScript-Limit)
// Besser: als String behalten bis zur finalen Berechnung
const cleaned = normalizeNumberString(value);
// Erst parseFloat verwenden, wenn man rechnen muss
Auf HTML-Entities achten
Webtabellen enthalten manchmal (geschütztes Leerzeichen) in Zahlen:
// "1 234 567" soll "1234567" werden
.replace(/ /g, " ")
.replace(/\s+/g, "")
Original beibehalten, wenn unsicher
Wenn der Wert nicht wie eine Zahl aussieht, unverändert zurückgeben:
if (!/[0-9]/.test(v)) return value;
// ...
if (!Number.isFinite(num)) return value;
Besser „N/A" als „N/A" belassen, als zu versuchen es zu parsen.
Zusammenfassung
| Szenario | Erkennung | Hinweise |
|---|---|---|
| Sowohl . als auch , | Letztes = Dezimal | Universal |
| Nur Komma, ≤2 Stellen danach | Dezimal | „1,23" |
| Nur Komma, 3+ Stellen danach | Tausender | „1,234" |
| Nur Punkt, ≤2 oder 4+ Stellen | Dezimal | „1.23", „3.14159" |
| Nur Punkt, exakt 3 Stellen | Tausender | „1.234" |
Der Drei-Stellen-Fall ist der pragmatische Kompromiss. Er funktioniert für die meisten realen Daten. Im Zweifelsfall ein Export-Profil verwenden, das zur Quellregion passt.
Für mehr zum Kopieren von Daten aus Websites nach Excel ohne Formatierungsprobleme, siehe unseren Leitfaden zur besten Chrome-Erweiterung zum Kopieren von Tabellen nach Excel.
Saubere Zahlenexporte ohne Ratespiel? Erfahren Sie mehr auf gauchogrid.com/de/html-table-exporter oder probieren Sie es kostenlos im Chrome Web Store.
Top comments (0)