Mit Dateinamen zu arbeiten scheint einfach – bis man in die unsichtbare Falle der Unicode-Normalisierung tappt.
Ich stieß auf dieses Problem, als ich in meinem Projekt Dateinamen zwischen UTF-8 und Windows ANSI verglich und escapen musste.
Alles sah korrekt aus – war es aber nicht.
Das Problem? Deutsche Umlaute wie ä, ö und ü – optisch identisch, intern jedoch unterschiedlich kodiert.


Das Problem

Unter Windows werden Dateinamen mit Umlauten in der Regel in NFC (Normalization Form C) gespeichert.
Das bedeutet, dass ein Zeichen wie ä (U+00E4) als ein einzelner, vorkomponierter Codepunkt repräsentiert wird.

Unter macOS hingegen werden Dateinamen meist in NFD (Normalization Form D) gespeichert.
In dieser Form wird ä zerlegt in zwei Codepunkte: - a (U+0061) - ¨ (U+0308, kombinierender Umlaut)

Obwohl beide Varianten gleich aussehen, unterscheidet sich "Müller.txt" byteweise zwischen macOS und Windows.


Kodierung der Umlaute: NFC vs. NFD

Zeichen NFC (vorkomponiert) Hex-Codepunkte NFD (zerlegt) Hex-Codepunkte
ä ä (U+00E4) 00E4 a + ¨ 0061 0308
ö ö (U+00F6) 00F6 o + ¨ 006F 0308
ü ü (U+00FC) 00FC u + ¨ 0075 0308
Ä Ä (U+00C4) 00C4 A + ¨ 0041 0308
Ö Ö (U+00D6) 00D6 O + ¨ 004F 0308
Ü Ü (U+00DC) 00DC U + ¨ 0055 0308

Warum das wichtig ist

Diese Unterschiede führen zu schwer auffindbaren Fehlern, etwa beim: - Vergleichen von Dateinamen über verschiedene Betriebssysteme hinweg
- Umwandeln von Texten zwischen UTF-8 und Windows-1252/ANSI
- Hashen und Prüfen von Gleichheit
- Vorverarbeiten von Texten in KI- oder NLP-Pipelines


Verbindung zur KI / NLP: Vokabular-Mismatch

Moderne Sprachmodelle (BPE, WordPiece, SentencePiece etc.) tokenisieren Texte anhand eines trainierten Vokabulars.
Wenn die Trainingsdaten in NFC vorlagen, deine Eingabe aber in NFD, entsteht ein unsichtbarer Mismatch.

Beispiel: - Das Modell-Vokabular enthält Müller (U+00FC). - Der macOS-Text enthält Müller (U+0075 + U+0308). - Der Tokenizer erkennt kein bekanntes Token und zerlegt das Wort falsch.

Das führt zu schlechterer Tokenisierung und ungenaueren Modell-Ergebnissen.


🔍 Tokenisierungs-Demo (Python)

Demonstration mit einem Hugging-Face-Tokenizer aus einem multilingualen Modell, z. B. bert-base-multilingual-cased:

from transformers import AutoTokenizer
import unicodedata

tokenizer = AutoTokenizer.from_pretrained("bert-base-multilingual-cased")

word_nfc = "Müller"                # vorkomponiertes ü (U+00FC)
word_nfd = unicodedata.normalize("NFD", word_nfc)  # zerlegt: u + U+0308

print("NFC Tokens:", tokenizer.tokenize(word_nfc))
print("NFD Tokens:", tokenizer.tokenize(word_nfd))

Mögliche Ausgabe:

NFC Tokens: ['Müller']
NFD Tokens: ['Mu', '̈', 'ller']

Die NFD-Version wird also in drei Tokens zerlegt – der kombinierende Umlaut ¨ wird als separates, unbekanntes Zeichen behandelt.
Das kann Embeddings und Modellvorhersagen massiv beeinflussen.


Die Lösung: Vor der Verwendung normalisieren

Normalisiere alles, was in deine Pipeline gelangt – Dateinamen, Benutzereingaben und Textdaten für NLP.
Standardmäßig ist NFC die sicherste Wahl, es sei denn, du hast einen guten Grund für NFD.


Python-Beispiel

import unicodedata

def normalize_filename(filename: str) -> str:
    return unicodedata.normalize('NFC', filename)

# Beispiel
macos_name = "Müller.txt"  # 'u' + kombinierender Umlaut
normalized = normalize_filename(macos_name)

print("Original:", [hex(ord(c)) for c in macos_name])
print("Normalisiert:", [hex(ord(c)) for c in normalized])

Ausgabe:

Original: ['0x4d', '0x75', '0x308', '0x6c', '0x6c', '0x65', '0x72']
Normalisiert: ['0x4d', '0xfc', '0x6c', '0x6c', '0x65', '0x72']

Java-Beispiel

import java.text.Normalizer;

public class UmlautNormalizer {
    public static String normalizeFilename(String filename) {
        return Normalizer.normalize(filename, Normalizer.Form.NFC);
    }

    public static void main(String[] args) {
        String macosName = "Mu\u0308ller.txt"; // 'u' + kombinierender Umlaut
        String normalized = normalizeFilename(macosName);

        System.out.println("Original: " + macosName);
        System.out.println("Normalisiert: " + normalized);
    }
}

Fazit

  • macOS nutzt standardmäßig NFD, Windows dagegen NFC.
  • NFC-Normalisierung sorgt für Konsistenz bei Vergleichen, Speicherung und KI-Training.
  • Texte sollten immer vor der Tokenisierung bereinigt werden – unsichtbare Unicode-Differenzen können Ergebnisse verfälschen.
  • Normalisierung erspart Stunden des Debuggens, in denen "ä" plötzlich ungleich "ä" ist.

Abschließende Gedanken

Unicode-Normalisierung klingt akademisch – ist aber essenziell für robuste Softwareentwicklung.
Ob bei Dateisynchronisation oder beim Training multilingualer Sprachmodelle:
Wenn deine Daten oder Benutzer deutsche Umlaute (oder andere Akzente) enthalten,
mache die NFC-Normalisierung zu einem festen Bestandteil deiner Vorverarbeitung.

Das spart Nerven – und verbessert die Ergebnisse deines Tokenizers.