Reguläre Ausdrücke
Kurz gesagt: Ein regulärer Ausdruck (Regex) ist ein Muster, das beschreibt, wie ein Text aussieht: Damit prüfst, durchsuchst, ersetzt und zerlegst du Text, statt viele ifs zu schreiben.
Problem
Sechzehn Zeilen für ein Datum
Das Buchungsformular will ein Datum in der Form tt.mm.jjjj. Du prüfst es Zeichen für Zeichen:
function isDate(s) { if (s.length !== 10) { return false; } for (let i = 0; i < s.length; i++) { const c = s[i]; if (i === 2 || i === 5) { if (c !== ".") { return false; } } else if (c < "0" || c > "9") { return false; } } return true;}
console.log(isDate("05.03.2026"));console.log(isDate("5.3.2026"));console.log(isDate("05/03/2026"));truefalsefalseSechzehn Zeilen, und die Funktion prüft nur das Format: zwei Ziffern, ein Punkt, zwei Ziffern, ein Punkt, vier Ziffern. Wenn du auch / statt eines Punkts annehmen willst oder einen Tag mit einer Ziffer, kommen noch einmal so viele Zeilen dazu. Eine Telefonnummer, eine E-Mail oder ein Kfz-Kennzeichen kosten dich jeweils noch so eine Funktion.
Die Idee
Ein Muster statt Code
Du beschreibst, wie der Text aussieht, und JavaScript übernimmt die Prüfung:
function isDate(s) { return /^\d{2}\.\d{2}\.\d{4}$/.test(s);}
console.log(isDate("05.03.2026"));console.log(isDate("5.3.2026"));console.log(isDate("05/03/2026"));truefalsefalseDu liest das Muster von links nach rechts: \d ist eine Ziffer, {2} heißt „zweimal“, \. ist ein Punkt. So ein Muster heißt regulärer Ausdruck (regular expression, Regex).
Das Muster steht zwischen zwei Schrägstrichen: /.../. Das ist ein Regex-Literal, so wie "..." ein String-Literal ist. test(text) gibt true zurück, wenn es das Muster irgendwo im Text findet. Deshalb beginnt das Muster mit ^ („der Anfang des Textes“) und endet mit $ („das Ende“): So muss es den ganzen Text abdecken, vom ersten bis zum letzten Zeichen.
Sieh es dir an
Wie das Muster sucht
replace prüft nicht den ganzen Text. Es sucht die Stücke, die zum Muster passen, und ersetzt sie. So geht es die Zeile entlang:
const line = "120 € am 05.03.2026, Frist 15.04.2026";const masked = line.replace(/\d{2}\.\d{2}\.\d{4}/g, "[Datum]");console.log(masked);Schritt für Schritt, in Worten
- Die Zeile ist eine Folge von Zeichen, jedes mit seiner eigenen Position.
- Ab Position 0: „1“ und „2“ sind Ziffern, aber dann kommt „0“ statt eines Punkts. Der Versuch scheitert.
- Ein neuer Versuch ab jedem nächsten Zeichen: „20 “, „0 “, „ €“… alles Fehlschläge.
- Ab Position 9 passen alle zehn Teile: der erste Treffer.
- Die Suche geht nach dem Treffer weiter, ab dem Komma, und findet den zweiten.
- replace mit dem Flag g ersetzt beide Treffer durch „[Datum]“.
Das Muster wird der Reihe nach ab jeder Position probiert. Sobald ein Teil nicht passt, scheitert der Versuch, und ein neuer beginnt beim nächsten Zeichen. Deshalb ist 120 nicht durchgekommen: zwei Ziffern, und dann 0 statt eines Punkts. Wenn es einen Treffer findet, geht die Suche danach weiter.
Das Flag g (global) nach dem Muster, /.../g, heißt „alle Treffer“. Ohne es ersetzt replace nur den ersten.
Die Idee
Zeichenklassen
In eckigen Klammern zählst du auf, welche Zeichen an einer Stelle erlaubt sind. Ein bulgarisches Kfz-Kennzeichen besteht aus einem oder zwei Buchstaben, vier Ziffern und zwei Buchstaben:
const plates = ["CA1234BX", "B7788KK", "CA12345BX", "ca1234bx"];for (const plate of plates) { console.log(plate, /^[A-Z]{1,2}\d{4}[A-Z]{2}$/.test(plate));}CA1234BX trueB7788KK trueCA12345BX falseca1234bx false| Muster | Trifft ein Zeichen |
|---|---|
[ABE] |
A, B oder E |
[0-9], [A-Z] |
aus dem Bereich: eine Ziffer, ein lateinischer Großbuchstabe |
[^0-9] |
alles außer einer Ziffer (^ am Anfang der Klammern) |
\d |
eine Ziffer, dasselbe wie [0-9] |
\s |
ein Leerzeichen, ein Tabulator, ein Zeilenumbruch |
\w |
ein lateinischer Buchstabe, eine Ziffer oder _ |
. |
irgendein Zeichen (außer einem Zeilenumbruch) |
Großbuchstaben drehen die Bedeutung um: \D ist „keine Ziffer“, \S ist „kein Leerzeichen“, \W ist „nicht \w“.
Vorsicht mit \w: In JavaScript sind das nur lateinische Buchstaben, ohne Umlaute und Akzente.
console.log(/^\w+$/.test("Berlin"));console.log(/^\w+$/.test("München"));console.log(/^\p{L}+$/u.test("München"));console.log(/^[A-Za-zü]+$/.test("München"));truefalsetruetrueFür Text mit Buchstaben wie „ü“ oder „ß“ zählst du sie in der Klasse auf, [A-Za-zü], oder du nimmst \p{L} mit dem Flag u: \p{L} ist ein Buchstabe aus jeder Sprache, und u (Unicode) sagt dem Muster, dass es solche Klassen verstehen soll.
Genauer, für Neugierige
[A-Z]ist ein Bereich nach den Codes der Zeichen. Er enthält nur die 26 lateinischen Buchstaben, ohne „Ä“, „Ö“, „Ü“ oder „ß“, die woanders in der Tabelle stehen. Für deutschen Text reicht[A-Za-z]deshalb nicht. Füge sie hinzu:[A-Za-zÄÖÜäöüß].- Echte bulgarische Kennzeichen verwenden nur 12 Buchstaben, die in Kyrillisch und Lateinisch gleich aussehen:
[ABEKMHOPCTYX].
Probier es aus
Der Punkt
Jemand hat den Backslash vor den Punkten vergessen:
Was gibt das Programm aus?
const pattern = /^\d{2}.\d{2}.\d{4}$/;console.log(pattern.test("05.03.2026"));console.log(pattern.test("05-03-2026"));console.log(pattern.test("05.03.26"));Ohne \ heißt der Punkt „irgendein Zeichen“, also kommt auch der Bindestrich durch, und 05-03-2026 ist wahr. Der dritte Text hat nur zwei Ziffern für das Jahr, und das Muster will vier. Die Prüfung scheint zu funktionieren, bis eine falsche Eingabe kommt. Teste das Muster deshalb auch mit Texten, die nicht durchkommen sollen.
Die Idee
Wie oft
Nach einem Zeichen oder einer Klasse sagst du, wie oft es sich wiederholen darf. Das ist ein Quantifizierer:
| Quantifizierer | Bedeutet |
|---|---|
? |
null- oder einmal (es kann da sein oder nicht) |
* |
null- oder mehrmals |
+ |
ein- oder mehrmals |
{4} |
genau 4-mal |
{1,2} |
1- bis 2-mal |
{2,} |
2-mal oder öfter |
Eine bulgarische Handynummer beginnt mit 0 oder +359, dann kommt 87, 88 oder 89 und noch 7 Ziffern:
const mobile = /^(\+359|0)8[789]\d{7}$/;console.log(mobile.test("0888123456"));console.log(mobile.test("+359877654321"));console.log(mobile.test("0888 123 456"));console.log(mobile.test("088812345"));truetruefalsefalse| heißt „oder“. Die Klammern halten \+359|0 zusammen: Ohne sie würde das „oder“ das ganze Muster in zwei Teile spalten. Klammern können noch mehr, das sehen wir in der nächsten Lektion. Das + am Anfang hat einen \, weil es sonst ein Quantifizierer ist.
Ein Quantifizierer wirkt auch auf eine ganze Gruppe in Klammern. Hier ist ein Preis mit oder ohne Nachkommateil:
const price = /^\d+([.,]\d{1,2})? €$/;console.log(price.test("12 €"));console.log(price.test("12.5 €"));console.log(price.test("12,50 €"));console.log(price.test(".50 €"));truetruetruefalse([.,]\d{1,2})? heißt „ein Punkt oder ein Komma und eine oder zwei Ziffern“, und dieser ganze Block darf fehlen.
Die Idee
Literal oder String
Im Literal /.../ ist der Backslash nur für das Muster da: \d ist eine Ziffer, \. ist ein Punkt, ohne Verdoppeln.
Manchmal baust du das Muster aus Text, zum Beispiel wenn ein Teil davon aus einer Variable kommt. Dann ist es ein String in Anführungszeichen für new RegExp(...), und es geht durch zwei Schichten: Zuerst liest JavaScript den String, dann das Muster. In einem String beginnt \ ein Sonderzeichen: \n ist ein Zeilenumbruch, \" ist ein Anführungszeichen. \d gehört nicht dazu, und JavaScript lässt den Backslash einfach weg, ohne Fehler und ohne Warnung:
const date = "05.03.2026";const pattern = new RegExp("^\d{2}\.\d{2}\.\d{4}$");console.log(pattern.source);console.log(pattern.test(date));^d{2}.d{2}.d{4}$falsesource zeigt das Muster, das RegExp bekommen hat: d{2} heißt „zwei Buchstaben d“. Deshalb schreibst du in einem String jedes \ des Musters als \\:
const date = "05.03.2026";console.log(/^\d{2}\.\d{2}\.\d{4}$/.test(date));console.log(new RegExp("^\\d{2}\\.\\d{2}\\.\\d{4}$").test(date));console.log(/^C:\\Temp$/.test("C:\\Temp"));truetruetrue| Du willst im Muster | Literal | String für new RegExp |
|---|---|---|
\d (eine Ziffer) |
/\d/ |
"\\d" |
\. (einen echten Punkt) |
/\./ |
"\\." |
\\ (einen echten Backslash) |
/\\/ |
"\\\\" |
Im Literal bekommt auch / einen \: /\//. Sonst würde es das Muster schließen.
Die Zeichen, die einen \ brauchen, wenn du sie wörtlich willst, sind . * + ? ( ) [ ] { } | ^ $ \.
Probier es aus
Finde den Fehler
0 € und 0 CentWähle die Zeile mit dem Fehler.
split mit /.../ nimmt einen regulären Ausdruck. /./ ist „irgendein Zeichen“, also ist jedes Zeichen ein Trennzeichen, und dazwischen steht nichts: Die Teile sind sechs leere Strings. Number("") ist 0 und kein Fehler, deshalb bricht das Programm nicht ab. Es gibt einen falschen Betrag aus. Die Korrektur ist split(/\./). Aber hier brauchst du gar kein Muster: price.split(".") sucht nach gewöhnlichem Text.const price = "12.50";console.log(price.split("."));console.log(price.split(/./));[ '12', '50' ][ '', '', '', '', '', '' ]Die Idee
Ersetzen und Zerlegen nach einem Muster
replace und split nehmen ein Muster. So räumst du Text auf, den Leute eintippen, wie es ihnen gerade passt, und zerlegst ihn:
const address = "Lieferung: Hauptstraße 12, Eingang B";console.log(address.replace(/\s+/g, " "));const items = "Brot, Milch;Kaffee Tee".split(/[,;\s]+/);console.log(`${items.length}: ${items.join(" | ")}`);console.log("Code SUMMER10, Code summer25".replace(/summer\d+/gi, "***"));Lieferung: Hauptstraße 12, Eingang B4: Brot | Milch | Kaffee | TeeCode ***, Code ***\s+ist „ein oder mehrere Leerzeichen“: Jede Gruppe von Leerzeichen wird zu einem.[,;\s]+ist „Kommas, Semikolons oder Leerzeichen, egal wie viele“: Das Trennzeichen kann jedes davon sein.
inach dem Muster ist ein Flag: Das Muster achtet nicht auf Groß- und Kleinschreibung. Flags schreibst du nebeneinander:giist „alle Treffer, ohne auf Groß- und Kleinschreibung zu achten“.
replace mit Text statt Muster ersetzt nur das erste Vorkommen. Für alle ist es replaceAll("alt", "neu"). Wenn du kein Muster brauchst, nimm Text: Dann musst du nicht an Punkte und Backslashes denken.
\b ist eine Wortgrenze: die Stelle zwischen einem Buchstaben und etwas, das kein Buchstabe ist. Damit ersetzt du nur ganze Wörter. Aber in JavaScript kennt \b, wie \w, nur lateinische Buchstaben ohne Umlaute und Akzente, auch mit dem Flag u:
const text = "Öl und Öle";console.log(text.replace(/Öl/g, "Fett"));console.log(text.replace(/\bÖl\b/g, "Fett"));console.log(text.replace(/(?<!\p{L})Öl(?!\p{L})/gu, "Fett"));Fett und FetteÖl und ÖleFett und ÖleOhne \b wird aus „Öle“ „Fette“. Mit \b sieht JavaScript „Ö“ nicht als Buchstaben und ersetzt nichts. Für solche Buchstaben schreibst du die Grenze mit einem Lookaround: (?<!\p{L}) ist „davor steht kein Buchstabe“, und (?!\p{L}) ist „danach steht kein Buchstabe“. Sie nehmen keine Zeichen. Sie prüfen nur.
Genauer, für Neugierige
\Bist das Gegenteil von\b: eine Stelle innerhalb eines Wortes. Auch es kennt nur lateinische Buchstaben ohne Umlaute und Akzente, deshalb macht es bei „ö“ genau das Falsche: Es überspringt das „ö“ in „schön“ und findet das in „öde“. Nur innerhalb eines Wortes ist(?<=\p{L})ömit dem Flagu: „davor steht ein Buchstabe“.^ist der Anfang des Textes und$das Ende:text.replace(/^0+/, "")entfernt die führenden Nullen.- Flags sind Buchstaben nach dem Muster:
g(alle),i(Groß- und Kleinschreibung egal),u(Unicode),munds(in der nächsten Lektion).(?i)am Anfang des Musters ist in Node 22 ein Fehler. "a|b".split(/|/)ist eine Falle wie der Punkt:|ist „oder“, und das Muster ist leer, also trennt es zwischen je zwei Zeichen."a|b".split("|")sucht nach gewöhnlichem Text und hat dieses Problem nicht.
console.log("schön öde".replace(/\Bö/g, "o"));console.log("schön öde".replace(/(?<=\p{L})ö/gu, "o"));console.log("a|b".split(/|/));console.log("a|b".split("|"));console.log("a a".replace("a", "b"), "a a".replaceAll("a", "b"));schön odeschon öde[ 'a', '|', 'b' ][ 'a', 'b' ]b a b bProbier es aus
Postleitzahl
Eine bulgarische Postleitzahl hat genau vier Ziffern. Was fehlt, damit das Programm für "1000" true ausgibt und für "100" oder "10000" false?
const code = "1000";
console.log(/^$/.test(code));\d{4}: genau vier Ziffern. Weil das Muster zwischen ^ und $ steht, kommen drei oder fünf Ziffern nicht durch.Übung
Prüfungen mit einem Muster
Löse die Aufgaben in deinem Editor. Lies Zeilen, solange es welche gibt, so (kopiere es vorerst einfach):
import { createInterface } from "node:readline";
const rl = createInterface({ input: process.stdin });for await (const line of rl) { // line ist die nächste Zeile, ohne "\n"}In der Lektion „Dateien“ liest du dieselben Zeilen aus einer Datei.
1. Datum. Prüfe, ob jedes Datum in der Form tt/mm/jjjj ist. Der Tag geht von 01 bis 31, der Monat von 01 bis 12. Tipp: Der Tag ist 0[1-9] oder [12]\d oder 3[01]. Im Literal schreibst du / als \/.
01/01/202032/01/202015/05/202101/01/2020 - Gültig32/01/2020 - Ungültig15/05/2021 - Gültig2. Handynummer. Leute schreiben Nummern mit Leerzeichen und Bindestrichen. Entferne sie mit replace und prüfe, was übrig bleibt, mit dem Muster für Handynummern aus der Lektion.
0888 123 456+359 87 765 432102 812 34560899-12-3450888 123 456 - gültig+359 87 765 4321 - gültig02 812 3456 - ungültig0899-12-345 - ungültig3. E-Mail. Prüfe, ob jede Zeile wie eine E-Mail aussieht: ein oder mehrere lateinische Buchstaben, Ziffern, ., + oder -, dann @, dann ein Domainname mit mindestens einem Punkt (firma.de, mail.firma.de).
maria.weber@firma.demaria@firma@firma.demaria weber@firma.demaria.weber@firma.de - jamaria@firma - nein@firma.de - neinmaria weber@firma.de - nein4. Einkaufsliste. Die Produkte sind durch Kommas, Semikolons, senkrechte Striche oder Leerzeichen getrennt, manchmal durch mehrere auf einmal. Gib sie nummeriert aus.
Brot, Milch;Kaffee Tee|Käse1. Brot2. Milch3. Kaffee4. Tee5. KäseGenauer, für Neugierige
- Das Muster für das Datum nimmt auch
31/02/2026an. Ob es den Tag im Monat gibt, kann eine Regex nicht vernünftig sagen. Das Muster ist nur das erste Sieb. Das eingebauteDatehilft hier nicht: Es lehnt den 31. Februar nicht ab, sondern verschiebt ihn auf den 3. März (die Monate inDatewerden ab 0 gezählt, deshalb ist Februar 1):
console.log(new Date(2026, 1, 31).toDateString());Tue Mar 03 2026Prüfe den Tag deshalb selbst: Bau das Datum und sieh nach, ob der Monat gleich geblieben ist.
- Die vollständige Regel für eine E-Mail-Adresse (RFC 5322) erlaubt an bestimmten Stellen Anführungszeichen, Kommentare und Leerzeichen. Ein Muster, das alles abdeckt, ist Hunderte Zeichen lang, und niemand pflegt es. Die echte Prüfung ist, eine E-Mail mit einem Bestätigungslink zu schicken.
Weitere Aufgaben
- Passwort. Prüfe, ob das Passwort mindestens 8 Zeichen lang ist und mindestens eine Ziffer hat. Mach es mit zwei
test:/^.{8,}$/und/\d/. Warum brauchst du beim zweiten kein^und$? - Uhrzeit. Prüfe eine Uhrzeit in der Form
hh:mm, von00:00bis23:59. - Nur Ziffern. Behalte von
"Tel.: +359 (88) 812-34-56"nur die Ziffern und das+, mit einemreplaceund[^...].
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Funktionen · Parameter und return · Exceptions · Strings
- Geht weiter in: Reguläre Ausdrücke: Gruppen und Ersetzen · Daten in Dateien · Array-Methoden · Netzwerkprogrammierung
- Aufgaben zum Thema: Text und Dateien