====== std.unicode ====== Unicode-Korrektheit kostet Platz: Die Tabellen aus ''UnicodeData.txt'' sind groß. Deshalb steckt sie **nicht** in ''[[lyx_-_programmiersprache:units:text|std.text]]'', sondern in dieser Unit, die man bewusst dazuholt. Wer nur UTF-8 zählen und schneiden will, zahlt nichts dafür. ''std.unicode'' liefert: * **Normalisierung** NFD, NFC, NFKD, NFKC — vollständige kanonische und Kompatibilitäts-Dekomposition, kanonische Sortierung der Kombinationszeichen, Komposition mit korrektem Blocking, Hangul algorithmisch * **Normalisierendes Vergleichen**: ''TextEqualsNormalized'', ''TextCompareNormalized'', ''TextStartsWithNormalized'' * **Groß-/Kleinschreibung** auf Codepoint- und ''Text''-Ebene — regelbasiert für ASCII und Latin-1 * **Klassifizierung**: Unicode-Whitespace, ASCII-Ziffer/-Buchstabe, dazu Unicode-bewusstes Trimmen Die Tabellen liegen in der generierten Unit ''std.unicode_data'' (aus ''UnicodeData.txt''), die dieser Unit gehört und nicht direkt benutzt wird. **Autor:** Andreas Röne\\ **Copyright:** 2024-2026 Andreas Röne → [[lyx_-_programmiersprache:units:text|std.text]] · [[lyx_-_programmiersprache:units:unicode_case|std.unicode_case]] · [[lyx_-_programmiersprache:units:grapheme|std.grapheme]] · [[lyx_-_programmiersprache:units|Standard Library]] ---- ===== Normalisierung ===== Dasselbe Schriftbild kann verschieden kodiert sein: „é" als ein Codepoint U+00E9 (**vorkomponiert**) oder als ''e'' + Combining Acute U+0301 (**zerlegt**). Ein Bytevergleich hält beide für verschieden. Normalisierung bringt sie auf eine gemeinsame Form. ^ Form ^ Bedeutung ^ | **NFD** | Kanonische Dekomposition — alles zerlegt | | **NFC** | Kanonische Dekomposition, danach Komposition — die kompakte Standardform | | **NFKD** | Wie NFD, zusätzlich Kompatibilitäts-Zerlegung (''fi'' → ''fi'', ''²'' → ''2'') | | **NFKC** | Wie NFKD, danach Komposition | import std.io; import std.text; import std.unicode; // "e" + U+0301 (Combining Acute) aus Codepoints gebaut — als Literal // geschrieben würde sich die zerlegte Form beim Kopieren still in die // vorkomponierte verwandeln, und das Beispiel zeigte nichts mehr. fn Zerlegt(): Text { var buf: int64 := alloc(8); var n: int64 := CodepointToUtf8(101, buf); // 'e' n := n + CodepointToUtf8(769, buf + n); // U+0301 return TextFromUtf8(buf, n); } fn main(): int64 { var a: Text := TextFromPchar("é"c); // U+00E9, vorkomponiert var b: Text := Zerlegt(); PrintLn(IntToStr(a.ByteLength())); // 2 PrintLn(IntToStr(b.ByteLength())); // 3 PrintLn(IntToStr(a.Equals(b))); // 0 — Bytevergleich PrintLn(IntToStr(TextEqualsNormalized(a, b))); // 1 — normalisiert gleich var n: Text := TextToNFC(b); PrintLn(IntToStr(n.ByteLength())); // 2 var d: Text := TextToNFD(a); PrintLn(IntToStr(d.ByteLength())); // 3 return 0; } ^ Signatur ^ Beschreibung ^ | ''TextToNFD(t: Text): Text'' | Kanonische Dekomposition + kanonische Sortierung | | ''TextToNFC(t: Text): Text'' | NFD, danach kanonische Komposition | | ''TextToNFKD(t: Text): Text'' | Kompatibilitäts-Dekomposition | | ''TextToNFKC(t: Text): Text'' | NFKD, danach Komposition | | ''TextEqualsNormalized(a, b): int64'' | Vergleich nach Normalisierung | | ''TextCompareNormalized(a, b): int64'' | Lexikografisch nach Normalisierung: ''-1''/''0''/''1'' | | ''TextStartsWithNormalized(t, prefix): int64'' | Präfixtest nach Normalisierung | Die 11.172 modernen Hangul-Silben (U+AC00–U+D7A3) werden **algorithmisch** zerlegt und zusammengesetzt (Unicode §3.12) — dafür braucht es keine Tabelle. > **NFC vergleicht, NFKC vereinheitlicht.** NFKC wirft Formatierungsunterschiede weg (''fi'' und ''fi'' werden gleich) und ist deshalb für Suchindizes und Bezeichner-Vergleiche gedacht, nicht zum Speichern von Nutzertext — die Umwandlung ist nicht umkehrbar. ---- ===== Groß-/Kleinschreibung ===== ^ Signatur ^ Beschreibung ^ | ''UnicodeToUpper(cp: int64): int64'' | Codepoint zu Groß — ASCII und Latin-1 Supplement | | ''UnicodeToLower(cp: int64): int64'' | Codepoint zu Klein — ASCII und Latin-1 Supplement | | ''TextToUpper(t: Text): Text'' | Dekodieren → wandeln → neu kodieren | | ''TextToLower(t: Text): Text'' | dito | > **Diese Umwandlung ist regelbasiert und reicht nur bis Latin-1.** Griechisch, Kyrillisch und Latin Extended bleiben unverändert: ''%%TextToUpper("Straße Über")%%'' liefert ''STRAßE ÜBER'' — das ''ß'' bleibt stehen, weil es keine 1:1-Großform hat. > > Für die vollständige Abdeckung ''[[lyx_-_programmiersprache:units:unicode_case|std.unicode_case]]'' verwenden (''TextToUpperFull''/''TextToLowerFull''). Die beiden Units sind bewusst getrennt: Beide bringen große generierte Tabellen mit, und zusammen übersetzt überschreitet das die Größengrenze des Compilers pro Übersetzungseinheit. ---- ===== Klassifizierung und Trimmen ===== ^ Signatur ^ Beschreibung ^ | ''UnicodeIsWhitespace(cp: int64): int64'' | Unicode-Eigenschaft ''White_Space'' — nicht nur ASCII | | ''UnicodeIsAsciiDigit(cp: int64): int64'' | ''0''–''9'' | | ''UnicodeIsAsciiLetter(cp: int64): int64'' | ''A''–''Z'', ''a''–''z'' | | ''TextTrimUnicode(t: Text): Text'' | Unicode-Whitespace an beiden Enden entfernen | | ''TextTrimStartUnicode(t: Text): Text'' | nur vorn | | ''TextTrimEndUnicode(t: Text): Text'' | nur hinten | Der Unterschied zu ''Text.Trim()'': Jenes kennt nur ASCII-Whitespace. Diese Funktionen entfernen auch geschütztes Leerzeichen, Halbgeviert-Abstand und die übrigen Zeichen mit der Eigenschaft ''White_Space'' — genau das, was aus kopiertem Text von Webseiten hereinkommt. ---- Letzte Aktualisierung: 2026-08-11