Kiste
EN

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.