Text ist die UTF-8-Ebene über den rohen Bytes. Sie validiert die Kodierung bei der Erzeugung und arbeitet auf Codepoints statt auf Bytes: CodepointCount, CodepointAt, SubstringCp zählen und schneiden dort, wo ein Zeichen anfängt — nicht mitten in einer Mehrbyte-Sequenz.
Die Unit braucht keine Unicode-Tabellen: UTF-8 zu prüfen und zu dekodieren ist reine Bytemuster-Arbeit. Alles, was Tabellen braucht — Normalisierung, volle Groß-/Kleinschreibung, Graphem-Segmentierung — liegt in eigenen Units, die man dazuholt:
| Aufgabe | Unit |
|---|---|
| Bytes, Besitz, Länge | std.strtype |
| UTF-8, Codepoints, Suchen/Schneiden/Splitten | std.text |
| Normalisierung NFC/NFD/NFKC/NFKD, Whitespace | std.unicode |
| Volle Groß-/Kleinschreibung, Caseless-Vergleich | std.unicode_case |
| Graphem-Cluster („was der Nutzer als ein Zeichen sieht„) | std.grapheme |
Intern speichert Lyx immer UTF-8. UTF-16 gibt es nur als Grenzformat (Windows-API, UTF-16-Dateien) — dafür die Konverter am Ende dieser Seite.
Autor: Andreas Röne
Copyright: 2024-2026 Andreas Röne
Text ist eine Klasse mit Referenzsemantik. Die freien Text*-Funktionen sind dünne Hüllen um die gleichnamigen Methoden — beide Schreibweisen sind gleichwertig.
import std.io;
import std.text;
fn Zeige(t: Text): void {
var p: pchar := t.ToPchar() as pchar;
PrintLn(p);
}
fn main(): int64 {
var t: Text := TextFromPchar("Größe"c);
PrintLn(IntToStr(t.ByteLength())); // 7 — Bytes
PrintLn(IntToStr(t.CodepointCount())); // 5 — Zeichen
PrintLn(IntToStr(t.IsValid())); // 1
Zeige(t.SubstringCp(0, 3)); // "Grö" — schneidet nach Codepoints
Zeige(t.AsciiUpper()); // "GRößE" — nur ASCII wird gewandelt
return 0;
}
Die HilfsfunktionZeigeist kein Zufall.ToPchar()hat den Rückgabetypint64; einas pchardirekt im Argument vonPrintLnwird nicht übernommen und würde die Adresse als Zahl ausgeben. Der Wert muss erst in einepchar-Variable.
| Funktion | Beschreibung |
|---|---|
TextFromPchar(p: int64): Text | Aus NUL-terminiertem UTF-8 |
TextFromUtf8(ptr: int64, n: int64): Text | Aus n rohen Bytes |
Ungültige Bytes führen nicht zum Fehlschlag: Die Bytes werden gespeichert und IsValid() meldet 0. Geprüft wird auf korrekte Start-/Folgebytes, Overlong-Kodierungen, Surrogate und den Bereich bis U+10FFFF.
| Signatur | Beschreibung |
|---|---|
ByteLength(): int64 | Länge in Bytes |
CodepointCount(): int64 | Anzahl der Codepoints |
IsValid(): int64 | 1, wenn der Inhalt gültiges UTF-8 ist |
Data(): int64 | Zeiger auf die Rohbytes (nicht NUL-terminiert) |
ByteAt(i: int64): int64 | Byte an Byte-Position i |
CodepointAt(idx: int64): int64 | Codepoint an Codepoint-Position idx |
ByteOffsetOfCodepoint(idx: int64): int64 | Byte-Offset, an dem Codepoint idx beginnt |
| Signatur | Beschreibung |
|---|---|
Add(other: Text): Text / Concat | Verkettung als neues Text |
SubstringCp(startCp, countCp): Text | Ausschnitt nach Codepoint-Positionen |
Equals(other: Text): int64 | Byte-Inhaltsvergleich |
EqualsPchar(p: int64): int64 | Vergleich gegen pchar |
StartsWith(prefix: Text): int64 | Präfixtest |
Compare(other: Text): int64 | Lexikografisch: -1, 0, 1 |
ToPchar(): int64 | Frische NUL-terminierte Kopie |
Free() | Puffer freigeben |
Equalsvergleicht Bytes. „é“ als ein Codepoint (U+00E9) und „é„ alse+ Combining Acute (U+0301) sind damit ungleich, obwohl sie gleich aussehen. Wer das gleichsetzen will, normalisiert vorher —TextEqualsNormalizedinstd.unicode.
| Signatur | Beschreibung |
|---|---|
Find(needle: Text): int64 | Byte-Offset des ersten Treffers, -1 wenn nicht gefunden |
FindCp(needle: Text): int64 | Codepoint-Position des ersten Treffers |
Contains(needle: Text): int64 | Enthaltenstest |
Trim(): Text | ASCII-Whitespace an beiden Enden entfernen |
Replace(old: Text, repl: Text): Text | Alle Vorkommen ersetzen |
SplitCount(sep: Text): int64 | Anzahl der Teile |
PartAt(sep: Text, idx: int64): Text | Einzelnes Teil — der bequeme Weg |
Split(sep, out: int64, maxParts: int64): int64 | Rohform: schreibt bis zu maxParts Feldblöcke à TEXT_PART_STRIDE Bytes nach out und liefert die Gesamtzahl der Teile zurück |
AsciiUpper(): Text / AsciiLower(): Text | Nur ASCII wandeln — Umlaute und Griechisch bleiben unangetastet |
var csv: Text := TextFromPchar("a,b,c"c);
var sep: Text := TextFromPchar(","c);
PrintLn(IntToStr(csv.SplitCount(sep))); // 3
Zeige(csv.PartAt(sep, 1)); // "b"
var alt: Text := TextFromPchar("b"c);
var neu: Text := TextFromPchar("X"c);
Zeige(csv.Replace(alt, neu)); // "a,X,c"
Splitliefert die Gesamtzahl der Teile, auch wenn nurmaxPartsdavon geschrieben wurden — der Rückgabewert taugt also zur Prüfung, ob der Puffer gereicht hat. Für den Normalfall istSplitCount+PartAtder einfachere Weg.
| Signatur | Beschreibung |
|---|---|
CodepointUtf8Len(cp: int64): int64 | Bytes, die cp in UTF-8 belegt (1–4; 0 wenn ungültig) |
CodepointToUtf8(cp: int64, dest: int64): int64 | Codepoint nach dest kodieren; liefert die Bytezahl |
| Operator | Methode | Ergebnis |
|---|---|---|
a + b | Add | neues Text |
a == b, a != b | Eq/Ne | Byte-Inhaltsgleichheit |
a < b, a ⇐ b, a > b, a >= b | aus Compare | lexikografisch |
a[i] | Get | Codepoint an Position i (nicht Byte!) |
Für UTF-16-Dateien und die Windows-API. Innerhalb von Lyx wird nichts als UTF-16 gehalten.
| Signatur | Beschreibung |
|---|---|
TextFromUtf16(ptr, byteLen, endian): Text | UTF-16 → Text mit vorgegebener Byte-Reihenfolge |
TextFromUtf16Bom(ptr, byteLen): Text | Byte-Reihenfolge aus einem führenden BOM ableiten und ihn entfernen |
TextUtf16Length(t: Text): int64 | Benötigte Bytes ohne BOM (für einen BOM 2 dazu) |
TextToUtf16(t, dest, endian): int64 | Nach dest kodieren, ohne BOM |
TextToUtf16Bom(t, dest, endian): int64 | Mit vorangestelltem U+FEFF |
Zwei Fallen bei der UTF-16-Umwandlung:
* Ohne BOM liestTextFromUtf16BomBig Endian — so schreibt es RFC 2781 für schlichtes „UTF-16“ vor. Von Windows erzeugte Dateien ohne BOM sind aber in aller Regel Little Endian. Wenn die Byte-Reihenfolge bekannt ist,TextFromUtf16mit ausdrücklicher Angabe verwenden.
* Fehlerhafte Eingabe schlägt nicht fehl, sie wird ersetzt. Ein unpaariges High- oder Low-Surrogate, ein abgeschnittenes Paar am Pufferende und ein einzelnes überzähliges Byte werden je zu U+FFFD. Das hält das Ergebnis als gültiges UTF-8 — wer schlechte Eingaben ablehnen statt ersetzen will, prüft das Ergebnis auf U+FFFD.
Letzte Aktualisierung: 2026-08-11