03 / GUIDES
Ein sicherer Workflow zum Testen regulärer Ausdrücke
Eine Schreiben-Testen-Refaktorisieren-Schleife für JavaScript-Regexes: realistische Beispieldaten, Hygiene gegen katastrophales Backtracking, Anker-Disziplin, Flag-Semantik und warum das Testen im Browser Muster privat hält.
Die Schleife: schreiben, testen, refaktorisieren
Ein verlässlicher Regex entsteht nie in einem Zug. Beginne mit dem kleinsten Muster, das ein echtes Beispiel matcht, führe es aus und weite oder enge es dann am nächsten Beispiel. Der Beispieltext ist die Spezifikation; das Muster ist die Implementierung.
Bei eingeschaltetem Auto-Run wertet der Tester kurz nach einer Tipppause aus. So bleibt die Folge aus Bearbeiten und Prüfen schnell, ohne bei jedem Tastendruck einen Lauf zu starten.
- Vom Beispiel ausgehenSchreibe das Muster an einem konkreten Beispiel, nicht aus dem Gedächtnis: Füge eine echte Zeile aus dem Log oder Dokument ein, das du parsen willst.
- Jede Änderung testenTeste nach jeder Änderung – ein Regex hat keine Compiler-Warnungen, die einzige Rückmeldung ist, was er matcht und was er verfehlt.
- Für Leser refaktorisierenRefaktorisiere fürs Lesen, sobald es passt: Benannte Capture Groups und nicht einfangende Gruppen machen ein funktionierendes Muster für den nächsten Leser überlebbar.
Katastrophales Backtracking respektieren
JavaScripts Regex-Engine backtrackt: Wenn ein Match scheitert, versucht sie es mit anderen Längen für jeden Quantifizierer erneut. Verschachtelte Quantifizierer wie (a+)+ über einem Beinahe-Treffer-String lassen die Zahl der Versuche exponentiell explodieren – ein Muster, das auf guter Eingabe sofort antwortet, kann auf schlechter jahrelang laufen. Angreifer nennen das ReDoS.
JavaScript Regex Tester führt Muster in einem eigenen Web Worker mit einem Zeitbudget von 650 Millisekunden aus und meldet einen Timeout bei ausufernden Ausdrücken. Ein Timeout bei realistischen Eingaben ist ein Warnsignal: Prüfe das Muster mit der produktiven Engine und deren Eingabegrenzen, bevor du es dort einsetzen.
- Verschachtelte QuantifiziererDie klassische Form ist eine quantifizierte Gruppe mit einem quantifizierten Token darin – (a+)+, (\w+)* –, bei der viele Aufteilungen desselben Strings das innere Muster erfüllen.
- Mehrdeutige AlternationMehrdeutige Alternation schafft das auch: (a|aa)+ gibt der Engine zwei Wege, jedes zusätzliche Zeichen zu matchen, und verdoppelt den Suchbaum pro Zeichen.
- Strukturelle FixesDie Fixes sind strukturell: Mach den inneren Match eindeutig, begrenze ihn mit expliziten Längen und teste immer die Beinahe-Eingabe, nicht nur gültige.
Verankern, bevor du dem Match traust
Ein Muster, das einen Teilstring findet, ist kein Muster, das einen Wert validiert. /\d+/ matcht innerhalb von abc123xyz; /^\d+$/ akzeptiert nur Ziffern. Die meisten Validierungsbugs sind fehlende Anker, und die meisten Extraktionsbugs sind Anker, die nicht da sein sollten.
- Anfang und Ende^ und $ pinnen den Match an Anfang und Ende des Strings – oder jeder Zeile, wenn das Multiline-Flag an ist, was still ändert, was „die ganze Eingabe“ bedeutet.
- Wortgrenzen\b-Wortgrenzen halten kurze Muster davon ab, innerhalb längerer Wörter zu matchen: \bcat\b findet das Tier, nicht das cat in concatenate.
- Suchen vs. validierenEntscheide Suchen versus Validieren von vornherein: Extraktionsmuster wollen meist keine Anker, Validierer fast immer beide.
Mit realistischen Beispieldaten testen
Ein Muster, das an drei sauberen Beispielen bewiesen wurde, scheitert in Produktion am vierten echten. Baue einen Beispielsatz, der die Eingabeverteilung abbildet: der längste legitime Wert, der leere String, Leerraum-Varianten, Unicode-Namen und der Beinahe-Treffer, der fast matchen sollte, aber nicht darf.
- Beinahe-TrefferNimm den Beinahe-Treffer auf: Ein E-Mail-Muster sollte gegen a@b und a b@c.d getestet werden, nicht nur gegen wohlgeformte Adressen.
- UnicodeNimm Unicode-Fälle auf, wenn die Daten sie enthalten: Mit u verarbeitet . Unicode-Codepunkte, aber \w ist keine allgemeine Klasse für Unicode-Buchstaben. Prüfe Namen mit Akzenten und verwende gegebenenfalls Property-Escapes wie \p{L}.
- Den Satz erneut laufen lassenBewahre den Beispielsatz neben dem Muster auf; wenn sich das Muster ändert, führe den ganzen Satz erneut aus, nicht nur den Fall, der die Änderung motiviert hat.
Flags ändern die Sprache, nicht nur die Suche
Flags sind Teil der Bedeutung des Musters. g entscheidet, ob du den ersten Match oder alle bekommst, i ignoriert Groß-/Kleinschreibung, m verankert ^ und $ an Zeilen, s lässt den Punkt Zeilenumbrüche überqueren, und u schaltet die Engine in einen Unicode-bewussten Modus mit strengerer Syntax.
Der Arbeitsbereich wertet das Muster mit den von dir gewählten Flags aus und listet jeden Match mit Start- und End-Offset, nummerierte und benannte Capture Groups mit eigenen Positionen sowie eine Live-Ersetzungsvorschau – eine Flag-Änderung zeigt sich also als konkreter Unterschied im Ergebnis, nicht als Theorie.
- g und lastIndexOhne g sehen test() und match() nur den ersten Treffer; mit g trägt ein Regex-Objekt lastIndex-Zustand, der wiederholte test()-Aufrufe abwechselnde Ergebnisse liefern lässt – ein berüchtigter Produktionsbug.
- s weitet den Punkts (dotAll) lässt . Zeilenumbrüche matchen, was den Appetit eines alten Musters still ausweiten kann, wenn es global aktiviert wird.
- u und Freundeu aktiviert Unicode-bewusste Escapes und strengere Syntax; \w oder \d werden dadurch nicht zu allgemeinen Klassen für Unicode-Buchstaben oder -Ziffern. Der Tester unterstützt außerdem d (Match-Indizes), y (Sticky) und v (Unicode-Mengen).
Muster und Beispiele lokal behalten
Testdaten sind oft echte Daten: Kunden-E-Mails, interne Log-Zeilen, Kennungen aus einem Incident Report. Sie in einen gehosteten Tester einzufügen sendet sie an fremde Infrastruktur. Ein browserbasierter Tester hält die Sitzung im Tab – Muster und Beispieltext werden von der Engine deines eigenen Geräts ausgewertet und nie übertragen.
Die lokale Laufzeit nennt ihre Obergrenzen explizit: Muster bis 4.096 Zeichen, Beispieltext bis 100.000 Zeichen, Ersetzungen bis 20.000 und höchstens 1.000 gemeldete Matches pro Lauf. Diese Grenzen sind auf Entwicklung und Debugging dimensioniert; Massen-Loganalyse gehört in ein lokales Skript, nicht in einen Browser-Tab.
Kopiere nur heraus, was du brauchst – das fertige Muster und den Ersetzungs-String –, und lass die Beispieldaten dort, wo sie begonnen haben: auf deinem Rechner.
Ein Muster lokal testenExtrahierte Daten landen meist in JSON.
Sobald ein Muster Werte aus Rohtext zieht, prüfe und diffe das resultierende Payload mit einer deterministischen lokalen Routine.