ToolZen ToolZen
Developer checker client

Regex Tester

Test regular expressions with real-time match highlighting, capture group display, replace output, and multi-flag support.

Advertisement (Top Banner)
/ /

Flags: g = global, i = case-insensitive, m = multiline, s = dotAll, u = unicode

Use $1, $2… for capture groups

Advertisement (Bottom)

Ein regulärer Ausdruck ist ein kleines Programm, und wie jedes Programm lässt er sich leichter reparieren, wenn man ihm beim Laufen zusehen kann. Muster tippen, Text einfügen, und die Treffer leuchten beim Schreiben auf. Hier läuft die Regex-Engine von JavaScript — und das ist wichtig, denn Regex-Dialekte unterscheiden sich stärker, als man denkt.

Manche Muster hängen stundenlang, deshalb läuft hier alles in einem Worker

Die Regex-Engine von JavaScript macht Backtracking, und Backtracking kann explodieren. Das Lehrbuchbeispiel ist (a+)+b: Gegen eine Zeichenkette aus dreißig "a" ohne "b" probiert die Engine jede Aufteilung dieser dreißig Zeichen zwischen innerem und äußerem Quantor durch — über eine Milliarde Pfade — bevor sie feststellt, dass es keinen Treffer gibt. Fünf Zeichen mehr, und es dauert dreißigmal so lange.

Das ist nicht theoretisch. Führe dieses Muster in einem naiven Tester aus, und der Browser-Tab friert vollständig ein, denn eine laufende Regex lässt sich aus JavaScript heraus nicht unterbrechen. Dieses Tool wertet dein Muster daher in einem Web Worker aus, auf einem eigenen Thread, mit einem Budget von einer Sekunde. Sprengt das Muster es, wird der Worker beendet und dir gesagt, dass das Muster pathologisch ist, statt dass du der Seite beim Sterben zusiehst. Der Tab bleibt die ganze Zeit bedienbar.

Die Lehre reicht weit über diese Seite hinaus: Erreichen benutzerdefinierte Muster je eine Regex-Engine auf deinem Server, lässt sich diese genauso aufhängen. Es hat einen Namen — ReDoS — und hat echte Dienste lahmgelegt.

Das g-Flag lässt deine Regex sich Dinge merken

Das ist der JavaScript-Regex-Fehler, der die meisten Debugging-Stunden kostet, und er wirkt wie Zauberei, wenn er zuschlägt. Ein Regex-Objekt mit g-Flag führt eine lastIndex-Eigenschaft, und sowohl .test() als auch .exec() rücken sie vor. Derselbe Test zweimal auf derselben Zeichenkette liefert also verschiedene Antworten.

Probier es in einer Konsole: const re = /a/g; re.test("a") gibt true zurück, dann re.test("a") false, dann wieder true. Es wechselt endlos. Der erste Aufruf trifft an Position 0 und setzt lastIndex auf 1; der zweite sucht ab Position 1, findet nichts und setzt lastIndex auf 0 zurück. Nichts ist kaputt — die Regex verhält sich exakt spezifikationsgemäß, und die Spezifikation überrascht.

Die Praxisregeln: Speichere nie eine Regex mit g-Flag in einer modulweiten Konstante und benutze sie mit .test() wieder. Willst du nur ein Ja/Nein, lass das g weg. Brauchst du es, baue die Regex bei jeder Verwendung neu oder setze lastIndex selbst zurück. Dieses Tool umgeht das Ganze, indem es bei jedem Tastendruck eine neue Regex baut.

JavaScript ist nicht PCRE

Ein Muster aus einer Perl-, PHP- oder Python-Antwort funktioniert hier oft, und gelegentlich nicht — auf Weisen, die leicht zu übersehen sind. JavaScript kennt keine atomaren Gruppen und keine possessiven Quantoren: genau die beiden Funktionen, die andere Dialekte anbieten, um die oben beschriebene Backtracking-Explosion zu verhindern. Es kennt keine Rekursion, die klassischen Muster für "ausgeglichene Klammern finden" sind also schlicht unmöglich.

Lookbehind gibt es in JavaScript, seit ES2018, aber es kam spät: Safari lieferte es erst mit Version 16.4 im März 2023 aus. Unterstützt du ältere iOS-Geräte, wird ein Muster mit (?<=...) nicht etwa keinen Treffer finden — es wirft beim Konstruieren der Regex einen SyntaxError und reißt dein Skript mit. Das ist ein deutlich lauteres Scheitern als ein fehlender Treffer, und gut zu wissen, bevor man eines ausliefert.

Die Flags, kurz gefasst

g findet jeden Treffer, statt beim ersten aufzuhören. i ignoriert Groß- und Kleinschreibung. m ändert die Bedeutung von ^ und $: Sie treffen an Zeilengrenzen statt nur am Anfang und Ende der gesamten Zeichenkette — meist genau das, was man beim Testen gegen eingefügten mehrzeiligen Text will. s lässt den Punkt auch Zeilenumbrüche treffen; ohne s überquert ein Punkt keinen Umbruch, was erstaunlich viele "Warum stoppt mein Muster am Zeilenende"-Fragen erklärt.

u schaltet korrekte Unicode-Behandlung ein. Ohne arbeitet die Engine in UTF-16-Codeeinheiten, ein einzelnes Emoji zählt also als zwei Zeichen und eine Zeichenklasse kann es in der Mitte zerschneiden. Enthält dein Text irgendetwas jenseits der Basic Multilingual Plane, willst du u.

Wann man keine Regex nehmen sollte

Regex findet Muster in flachem Text. Sie kann nicht zählen, sich keine beliebige Verschachtelung merken und hat kein Konzept von Struktur. HTML, JSON und Quellcode verschachteln sich alle — deshalb funktioniert jeder Versuch, sie mit einer Regex zu parsen, an den Beispielen und scheitert an den echten Daten. Nimm einen Parser; jede Sprache hat einen.

E-Mail-Adressen verdienen eine eigene Warnung. Die Grammatik in RFC 5322 erlaubt Kommentare, quotierte Zeichenketten und verschachtelte Konstrukte, und die Regexes, die sie getreu umsetzen, gehen in die Tausende von Zeichen. Zugleich kann eine gültige Adresse trotzdem zurückkommen und eine "ungültig aussehende" tadellos zustellbar sein. Prüfe, dass ein @ mit etwas auf beiden Seiten da ist, und schicke dann eine Bestätigungsmail — der einzige Test, der wirklich etwas beweist.

Häufige Fragen

Werden mein Muster oder mein Text an einen Server geschickt?

Nein. Dieses Tool ist als "client" gekennzeichnet: Das Muster läuft in einem Web Worker in deinem eigenen Browser — ein separater Thread auf deiner Maschine, keine entfernte. Es wird nichts übertragen und nichts gespeichert.

Warum bekomme ich "Dieses Muster braucht zu lange"?

Dein Muster hat das Ein-Sekunden-Budget gesprengt, was praktisch immer katastrophales Backtracking bedeutet und nicht eine wirklich große Aufgabe. Suche nach verschachtelten Quantoren — eine Gruppe, die auf + oder * endet und selbst wiederholt wird, etwa (a+)+ oder (\d*)* — und nach Alternativen, deren Zweige denselben Text treffen können. Den inneren Teil spezifischer zu machen lässt die Laufzeit meist von Minuten auf Mikrosekunden fallen.

Mein Muster geht hier, aber nicht in meinem Code. Warum?

Zwei übliche Verdächtige. Der erste ist lastIndex, siehe oben: Dein Code verwendet vermutlich ein g-Flag-Regex-Objekt wieder, wo dieses Tool jedes Mal ein neues baut. Der zweite ist Escaping. In einem JavaScript-String-Literal ist "\d" nur "d" — du brauchst "\\d", oder besser ein Regex-Literal wie /\d+/, wo es gar keine String-Escapes gibt. Das Musterfeld hier nimmt das rohe Muster, ganz ohne String-Schicht dazwischen.

Wie treffe ich einen wörtlichen Punkt oder ein Sonderzeichen?

Setz einen Backslash davor: \. trifft einen Punkt statt "irgendein Zeichen". Diese Behandlung brauchen . * + ? ^ $ { } ( ) | [ ] \ — und innerhalb einer Zeichenklasse gelten lockerere Regeln, [.] geht also auch und liest sich oft besser. Escapest du eine Zeichenkette aus Benutzereingaben, tu es programmatisch statt von Hand.

Warum trifft $ nicht am Ende jeder Zeile?

Weil ^ und $ ohne m-Flag an der gesamten Zeichenkette ankern statt an jeder Zeile. Füge m zu den Flags hinzu, und sie treffen an jeder Zeilengrenze. Das bringt ständig Leute aus dem Tritt, die ein Muster gegen eingefügten mehrzeiligen Text testen — denn das Muster stimmt, die Flags nicht.