std.text

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


Erzeugen und ausgeben

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 Hilfsfunktion Zeige ist kein Zufall. ToPchar() hat den Rückgabetyp int64; ein as pchar direkt im Argument von PrintLn wird nicht übernommen und würde die Adresse als Zahl ausgeben. Der Wert muss erst in eine pchar-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.


Methoden

Abfragen

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

Ableiten und Vergleichen

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
Equals vergleicht Bytes. „é“ als ein Codepoint (U+00E9) und „é„ als e + Combining Acute (U+0301) sind damit ungleich, obwohl sie gleich aussehen. Wer das gleichsetzen will, normalisiert vorher — TextEqualsNormalized in std.unicode.

Suchen, Trimmen, Ersetzen, Splitten

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"

Split liefert die Gesamtzahl der Teile, auch wenn nur maxParts davon geschrieben wurden — der Rückgabewert taugt also zur Prüfung, ob der Puffer gereicht hat. Für den Normalfall ist SplitCount + PartAt der einfachere Weg.

Codepoint-Hilfsfunktionen

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

Operatoren

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!)

UTF-16 — nur an der Grenze

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 liest TextFromUtf16Bom Big 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, TextFromUtf16 mit 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