Standard-Bibliothek — `regex`
Verfügbar — implementiert in Phase B.7.4. Vollständige Spec in
docs/regex.md.
Reguläre Ausdrücke (kurz: Regex) sind Muster, die bestimmte Texte beschreiben — „eine oder mehrere Ziffern", „ein Wort gefolgt von = und einer Zahl", „eine Email-Adresse". Mit Regex kannst du:
- prüfen, ob ein Text einem Muster entspricht (Validierung)
- Treffer aus einem grösseren Text extrahieren (Suche)
- Treffer ersetzen (Bereinigung, Transformation)
- Texte an Mustern zerlegen (komplexes Splitting)
26.1 Dein erstes Muster
nutze regex
sag regex.passt("hund", "ein hund läuft") // wahr (anywhere-Match)
sag regex.passt("katze", "ein hund läuft") // falsch
passt gibt wahr zurück, sobald das Muster irgendwo im Text matcht. Das ist der häufigste Fall. Wenn du einen exakten Vollmatch willst, nutzt du Anker:
sag regex.passt("^hund$", "hund") // wahr (Vollmatch)
sag regex.passt("^hund$", "ein hund") // falsch
26.2 Wichtig: Backslash und Triple-Quoted-Strings
Viele Regex-Tutorials nutzen Sonderzeichen mit Backslash: \d für Ziffern, \w für Wort-Zeichen, \s für Leerzeichen. In Kiste-Strings ist \ aber das Escape-Zeichen — "\d" wirft einen Lex-Fehler („unbekannte Escape-Sequenz").
Lösung: triple-quoted Strings """...""" lassen Backslashes literal durch:
// EMPFOHLEN — triple-quoted, lesbar:
nimm muster = """\d+"""
sag regex.treffer(muster, "Bob ist 42") // "42"
// funktioniert auch, ist aber laut:
sag regex.treffer("\\d+", "Bob ist 42") // "42"
Faustregel: für Regex-Muster immer """...""" nutzen. Sieht aus wie das Muster in jeder Regex-Referenz, keine Backslash-Buchhaltung.
26.3 Treffer extrahieren
nutze regex
nimm zahl = """\d+"""
// Erster Treffer (oder nichts, wenn keiner):
sag regex.treffer(zahl, "1, 22, 333") // "1"
sag regex.treffer("xyz", "abc") // nichts
// Alle Treffer:
sag regex.treffer_alle(zahl, "1, 22, 333") // ["1", "22", "333"]
26.4 Capturing-Gruppen — Teile zerlegen
Mit Klammern (...) markierst du Gruppen, deren Inhalt du einzeln auslesen kannst:
nimm kv = """(\w+)=(\d+)"""
// Gruppen vom ERSTEN Treffer:
sag regex.gruppen(kv, "alter=42 punkte=99")
// ["alter=42", "alter", "42"]
// ↑ voll ↑ Gruppe 1 ↑ Gruppe 2
// Gruppen von ALLEN Treffern:
sag regex.treffer_alle_gruppen(kv, "alter=42 punkte=99")
// [["alter=42", "alter", "42"], ["punkte=99", "punkte", "99"]]
Position 0 in jeder Gruppen-Liste ist immer der vollständige Treffer. Position 1+ sind die Gruppen, die du in Klammern gesetzt hast.
26.5 Ersetzen
sag regex.ersetzt("a", "abracadabra", "X") // "XbrXcXdXbrX"
sag regex.ersetzt_erstes("a", "abracadabra", "X") // "Xbracadabra"
// Backreferences im Ersatz: $1, $2, ... verweisen auf Gruppen:
nimm kv = """(\w+)=(\d+)"""
sag regex.ersetzt(kv, "name=42, alter=99", "$1:$2")
// "name:42, alter:99"
26.6 Zerlegen
regex.zerlegt ist wie text.zerlegt aus Kapitel 19 — aber mit einem Regex-Trenner statt einem Literal-Trenner. Das macht es mächtig:
// An jedem Whitespace zerlegen, egal wieviel:
sag regex.zerlegt("""\s+""", "ein zwei drei")
// ["ein", "zwei", "drei"]
// CSV mit optionalen Leerzeichen nach Komma:
sag regex.zerlegt(""",\s*""", "a, b,c, d")
// ["a", "b", "c", "d"]
26.7 Sicherheit: regex.maskiert für Nutzer-Eingaben
Sobald ein Muster aus einer Nutzer-Eingabe oder Variable kommt, muss es maskiert werden — sonst werden Sonderzeichen in der Eingabe zu Regex-Befehlen.
nimm version = "1.2.3"
// FALSCH — die Punkte werden zum Wildcard:
sag regex.passt(version, "1X2X3") // wahr — sollte falsch sein!
// RICHTIG — maskiert macht Sonderzeichen zu Literalen:
sag regex.passt(regex.maskiert(version), "1X2X3") // falsch
sag regex.passt(regex.maskiert(version), "1.2.3") // wahr
Faustregel: Such-Felder, Filter-UIs, Konfigurationen — überall wo Text aus Variablen ins Muster fliesst, gehört regex.maskiert(...) davor.
26.8 Sehr wichtig: Kiste nutzt RE2, nicht PCRE
Wer Regex aus Perl, Python oder JavaScript kennt, wird ein paar Features bei Kiste nicht finden:
| Feature | PCRE-Syntax | Status in Kiste |
|---|---|---|
| Lookahead | (?=...) |
nicht unterstützt |
| Lookbehind | (?<=...) |
nicht unterstützt |
| Backreferences im Muster | (.)\1 |
nicht unterstützt |
sag regex.gültig("(?=lookahead)") // falsch — RE2 lehnt ab
Warum so? RE2 garantiert dafür linear-zeitige Ausführung. Pathologische Muster wie (a+)+b auf aaaaaaaaaaaa! legen Java/Python/Node sekundenlang lahm — RE2 nicht. Das ist ein Sicherheits-Feature gegen ReDoS-Angriffe (Regex Denial of Service).
Faustregel: wenn ein Tutorial-Muster aus dem Internet bei Kiste „komische Fehler" wirft, liegt's fast immer an einem dieser nicht unterstützten Features. Das ist gewollt.
26.9 Inline-Flags
RE2 erlaubt Flags innerhalb des Musters:
sag regex.passt("(?i)HUND", "ein hund") // wahr (case-insensitive)
sag regex.treffer_alle("(?i)hund", "Hund hund") // ["Hund", "hund"]
| Flag | Bedeutung |
|---|---|
(?i) |
Case-insensitive |
(?m) |
^/$ matchen Zeilen-Anfang/Ende statt Text-Grenzen |
(?s) |
. matcht auch Newlines |
26.10 gültig — Pre-Flight-Check
Wenn ein Muster aus Nutzer-Eingabe kommt (IDE-Suche, Filter-UI), prüfst du erst:
nimm muster = lese_user_eingabe()
wenn nicht regex.gültig(muster) {
sag "Ungültiges Suchmuster, bitte korrigieren"
} sonst {
nimm treffer = regex.treffer_alle(muster, dokument)
/* sicher — kompiliert garantiert */
}
26.11 Was nicht in regex v1 ist
- Named-Captures auslesen (
(?P<name>...)mit Karten-Rückgabe) — die Syntax kompiliert (RE2 unterstützt sie), aber die API ist in v1 noch positions-basiert. Kommt später additiv. - Replace-mit-Funktion (Callback der den Treffer transformiert) — kommt später.
- Treffer-Positionen (
treffer_position) — selten gebraucht, kommt bei Bedarf.