Ein Regex ist ein Muster zum Abgleichen von Text
Reguläre Ausdrücke beschreiben die Form von Zeichenketten: „vier Ziffern", „ein Wort gefolgt von einem Komma", „alles, was wie eine E-Mail aussieht". In JavaScript gibt es zwei Wege, einen regulären Ausdruck zu erstellen:
Die Literalschreibweise — Pattern zwischen Schrägstrichen, Flags hinter dem schließenden Slash — ist das, was du in der Praxis meistens verwendest. Zu new RegExp(...) greifst du dann, wenn das Pattern selbst dynamisch ist, also zum Beispiel aus Benutzereingaben oder einer Variablen zusammengebaut wird.
Das i am Ende ist ein Flag. i steht für Groß-/Kleinschreibung ignorieren. Zu den Flags gleich mehr.
test: Gibt es einen Treffer?
Die einfachste Frage, die du an eine Regex stellen kannst, lautet: „Enthält dieser String einen Treffer?" Genau dafür gibt es test:
\d steht für „eine beliebige Ziffer". test liefert ausschließlich true oder false – mehr nicht. Wenn du nur eine Ja-oder-Nein-Antwort brauchst – etwa beim Validieren eines Feldes oder beim Filtern eines Arrays – ist test genau das richtige Werkzeug.
match: Den gefundenen Text herausziehen
Wenn du den Treffer selbst brauchst, nimmst du die String-Methode match:
Ohne das g-Flag liefert match ein Array mit dem ersten Treffer plus Metadaten (Index, Input). Mit g bekommst du dagegen alle Treffer als einfaches String-Array zurück. Findet sich nichts, ist das Ergebnis null — nicht etwa ein leeres Array. Darauf solltest du unbedingt prüfen:
Flags: So steuerst du das Matching-Verhalten
Flags stehen direkt hinter dem schließenden Slash und verändern, wie der reguläre Ausdruck matcht. Diese hier brauchst du am häufigsten:
g— global, findet alle Treffer statt nur den ersten.i— ignoriert Groß- und Kleinschreibung.m— multiline, damit^und$auf Zeilenanfang bzw. -ende passen, nicht nur auf Anfang und Ende des gesamten Strings.s— dotall, sodass.auch Zeilenumbrüche matcht.u— Unicode-Modus, nötig für viele Emojis und Nicht-ASCII-Zeichen.
Ohne m bindet ^ nur an den Anfang des gesamten Strings. Mit m gilt der Anker dagegen für jeden Zeilenanfang – deshalb werden hier sowohl Roses als auch Violets erkannt.
Zeichenklassen und Quantifizierer
Die wichtigsten Bausteine für fast jedes Pattern:
\dZiffer,\wWortzeichen (Buchstabe, Ziffer oder Unterstrich),\sWhitespace.[abc]eines der Zeichen a, b oder c.[^abc]alles außer diesen.[a-z]ein Bereich..beliebiges Zeichen außer Zeilenumbruch.*null oder mehr,+eins oder mehr,?null oder eins.{3}genau drei,{2,5}zwischen zwei und fünf,{2,}zwei oder mehr.^Anfang,$Ende.
Und so sieht das kombiniert aus:
\b steht für eine Wortgrenze – die unsichtbare Linie zwischen einem Wortzeichen und einem Nicht-Wortzeichen. Praktisch, wenn du nur ganze Wörter matchen willst.
Capture Groups: Teile eines Matches merken
Runde Klammern erzeugen eine Gruppe, die das Gematchte festhält. exec und match geben dir diese Captures zusammen mit dem Gesamttreffer zurück:
Index 0 enthält den kompletten Treffer, danach bekommt jede Gruppe ihren eigenen Slot. Sobald du mehr als zwei Gruppen hast, wird das Abzählen über die Positionen schnell mühsam – vergib ihnen deshalb lieber Namen:
Benannte Gruppen lesen sich an der Aufrufstelle deutlich angenehmer und überstehen auch ein Umsortieren des Musters.
replace: Gefundenen Text umschreiben
replace erwartet ein Muster und einen Ersatz. Der Ersatz kann entweder ein String oder eine Funktion sein:
Ohne g wird nur der erste Treffer ersetzt. Ein klassischer Stolperstein: Man vergisst das Flag und wundert sich dann, warum die zweite E-Mail immer noch falsch aussieht.
Ersetzungsstrings unterstützen Rückverweise. $1, $2 usw. beziehen sich auf Capture Groups, $<name> auf benannte Gruppen:
Wenn du mehr brauchst als simples Suchen-und-Ersetzen, übergib einfach eine Funktion. Sie bekommt den Treffer und die Capture Groups als Argumente:
Der Unterstrich steht für den kompletten Treffer (den wir hier nicht brauchen); n ist die erste Capture Group. Dieses Muster – Regex kombiniert mit einer Replacer-Funktion – deckt die meisten realen Text-Transformationen ab.
matchAll: alle Treffer samt Gruppen
String.prototype.matchAll liefert einen Iterator über jeden einzelnen Treffer – inklusive der Capture Groups. Genau das kann match mit dem g-Flag nämlich nicht:
matchAll funktioniert nur mit dem g-Flag. Fehlt es, gibt's einen TypeError. Wenn du wahlfreien Zugriff statt Iteration brauchst, pack das Ganze per Spread in ein Array ([...text.matchAll(email)]).
Sonderzeichen escapen
Zeichen wie . * + ? ( ) [ ] { } | \ ^ $ haben in einer Regex eine besondere Bedeutung. Um sie wortwörtlich zu matchen, musst du sie mit einem Backslash escapen:
Die nicht escapete Variante matcht auch examplexcom, weil . schlicht "irgendein Zeichen" bedeutet. Solche Bugs passieren ständig – und bleiben oft unbemerkt. Wenn dein Regex plötzlich zu viel matcht, schau zuerst nach einem nicht escapeten ..
Sobald du ein Pattern aus Nutzereingaben zusammenbaust, musst du diese Eingaben escapen – sonst kann jemand eigene Regex-Syntax einschleusen:
$& in einem Ersatzstring ist die Kurzschreibweise für „der komplette Treffer".
Lookahead und Lookbehind
Manchmal willst du Text nur dann matchen, wenn danach (oder davor) etwas Bestimmtes steht – ohne dass dieses Etwas selbst Teil des Treffers wird. Genau dafür gibt es Lookarounds:
(?= ...)ist ein positiver Lookahead: „gefolgt von".(?<= ...)ist ein positiver Lookbehind: „vorangegangen von".(?! ...)und(?<! ...)sind die jeweils negativen Varianten.
Lookarounds verbrauchen keine Zeichen. Der Teil, auf den du „schaust", bleibt also für den Rest des Patterns verfügbar.
Kurz zum Thema E-Mail-Validierung
Diese Frage taucht immer wieder auf: „Gib mir mal eine Regex, die E-Mail-Adressen validiert." Die ehrliche Antwort lautet: Lass es. Die tatsächliche Grammatik von E-Mail-Adressen ist ein echtes Biest, und jede Regex, die kurz genug zum Lesen ist, liegt in irgendeine Richtung daneben. Für eine Formularvalidierung reicht ein pragmatisches Pattern völlig aus:
Lies das so: „ein paar Nicht-Whitespace-Zeichen ohne @, dann ein @, noch mehr davon, ein Punkt, nochmal mehr davon." Damit erwischst du offensichtliche Tippfehler, ohne so zu tun, als würdest du RFC 5322 umsetzen. Für echte Validierung schickst du einfach eine Bestätigungsmail.
Typische Stolperfallen
Ein paar Fallstricke, die du dir merken solltest:
- Das
g-Flag beireplaceodermatchAllvergessen. Dann greift nur der erste Treffer – oder du kassierst einenTypeError. lastIndexbei globalen Regexes hat ein Gedächtnis. Ein regulärer Ausdruck mit dem Flaggoderymerkt sich zwischentest- bzw.exec-Aufrufen, wo er stehen geblieben ist. Verwende denselben Regex also nicht für unabhängige Strings – erstelle lieber einen frischen oder greif zumatchAll.- Nicht escapte Punkte und Slashes in dynamischen Patterns. Escape Nutzereingaben immer, bevor du sie in
new RegExp(...)reinstopfst. - Catastrophic Backtracking. Verschachtelte Quantoren wie
(a+)+können bei bösartigem Input den ganzen Tab einfrieren. Wenn sich ein Regex träge anfühlt – vereinfachen.
Weiter geht's: Datum und Uhrzeit
Regex kümmert sich um die Form von Text; echte Daten bringen aber auch Zeitstempel mit, die geparst, formatiert und berechnet werden wollen. Die nächste Seite zeigt dir Date, Intl.DateTimeFormat und das mentale Modell, mit dem du Zeitzonen-Bugs aus dem Weg gehst.
Häufig gestellte Fragen
Wie legt man einen regulären Ausdruck in JavaScript an?
Es gibt zwei Wege. Die Literal-Schreibweise mit Slashes: /hello/i. Oder den Konstruktor, der einen String entgegennimmt: new RegExp('hello', 'i'). Das Literal nimmst du bei festen Mustern, den Konstruktor dann, wenn du das Pattern zur Laufzeit aus einer Variable zusammenbauen musst.
Was ist der Unterschied zwischen test, match und exec?
regex.test(str) liefert einen Boolean zurück – am schnellsten, wenn dich nur interessiert, ob überhaupt ein Match existiert. str.match(regex) gibt ein Array der Treffer zurück (oder null). regex.exec(str) liefert einen Treffer nach dem anderen inklusive Capture Groups und merkt sich mit dem g-Flag über lastIndex, wo es beim nächsten Aufruf weitermacht.
Wie ersetze ich per Regex alle Vorkommen in einem String?
Mit dem g-Flag: str.replace(/foo/g, 'bar'). Ohne g wird nur der erste Treffer ersetzt. Alternativ geht str.replaceAll(/foo/g, 'bar') – Achtung: wenn du replaceAll mit einem Regex aufrufst, muss das g-Flag gesetzt sein, sonst fliegt ein Fehler.
Was sind Capture Groups in JavaScript-Regex?
Klammern im Pattern erzeugen Capture Groups, die sich merken, was sie getroffen haben. /(\d{4})-(\d{2})/.exec('2024-11') gibt ein Array zurück, in dem Index 1 '2024' und Index 2 '11' ist. Mit (?<year>\d{4}) kannst du die Gruppen auch benennen und bequem über match.groups.year darauf zugreifen.