Was sind reguläre Ausdrücke eigentlich?
Reguläre Ausdrücke (kurz Regexes) sind Strings, die als DSL (Domain-Specific Language, also eine domänenspezifische Sprache) funktionieren, um bestimmte häufige Aufgaben in anderen Strings zu erledigen. Eine DSL kann man auch als “eine Programmiersprache innerhalb einer Programmiersprache” beschreiben.
Im Fall von Regexes kann die äußere Programmiersprache jede beliebige Sprache sein, die den Typ String unterstützt – sie muss eben nur Regexes unterstützen. Nahezu alle populären Programmiersprachen unterstützen Regexes, was sie so nützlich macht. Die innere Sprache der Regexes besteht nur aus String, wobei bestimmte Zeichen eine besondere Bedeutung haben.
Zum Beispiel bedeutet im String ".*@.*\\.com" der . “ein beliebiges Zeichen”, das * bedeutet “beliebig oft .* also “beliebig viele beliebige Zeichen”. Dann haben wir ein nicht-spezielles Zeichen @, dann wieder .* gefolgt von \\, was “das nächste Zeichen escapen und als nicht-spezielles Zeichen behandeln” bedeutet – \\. zusammen liest sich also wie ein normaler . ohne die besondere Bedeutung “beliebiges Zeichen”. Zum Schluss kommt com, was einfach eine Reihe von Zeichen ohne besondere Bedeutung ist. Insgesamt ist diese Regex ein einfacher Matcher für jede E-Mail-Adresse, die mit .com endet und irgendwo ein @ enthält.
Was kann ich mit der “Regex-DSL” machen?
ℹ️ Wenn du Ruby installiert hast (auf Macs ist es vorinstalliert), kannst du
irbeingeben, um eine interaktive Ruby-Shell zu starten und mit den folgenden Beispielen herumzuspielen.
Es gibt drei Hauptfunktionen, mit denen jeder Regex-String verwendet werden kann:
matches: Gegeben eine Regex und einen anderen String, prüft diese Funktion, ob der gegebene String zur Regex “passt”. Das heißt, wenn es “irgendeinen” Teil im gegebenen String gibt, der zur angegebenen Regex passt, gibt sietruezurück, sonstfalse. Zum Beispiel in Ruby (womatchesalsmatch?heißt – das?ist Teil des Funktionsnamens):
/.*@.*\\.com/.match?('[email protected]') # => false /.*@.*\\.com/.match?('[email protected]') # => truecaptures: Gegeben eine Regex und einen anderen String, kann diese Funktion Teilstrings aus dem gegebenen Text auslesen, die zu markierten Teilen der gegebenen Regex passen. Die Teile in der Regex werden mit(und)markiert. Sie werden “Capture Groups” (Erfassungsgruppen) genannt. Zum Beispiel in Ruby (wo aufcapturesüber dasMatch-Objekt zugegriffen wird):
/(.*)@(.*)\\.com/.match('[email protected]').captures # => ["queenie.goldstein", "ilvermorny"]replace: Gegeben eine Regex und ein Template-String, kann diese Funktion Treffer automatisch mit einem gegebenen String ersetzen, wobei sogar die Capture Groups über$1,$2oder in manchen Sprachen auch\\1,\\2usw. referenziert werden können. Zum Beispiel in Ruby (woreplacealsgsubheißt):
'[email protected]'.gsub(/(.*)@(.*)\\.com/, '\\1@\\2.org') # => "[email protected]"Wie sieht die “Regex-DSL” aus?
Es gibt jede Menge nützliche “Cheat Sheets” mit tollen Beispielen dafür:
Grundsätzlich gibt es 5 verschiedene Arten von DSL-Komponenten zu verstehen:
1. Zeichen-/Gruppenmodifikatoren (z.B. *, +, {,}, ?)
Der Standard-“Baustein” von Regexes sind Zeichen. Nach jedem Zeichen kannst du einen Modifikator schreiben, der angibt, wie oft das vorangehende Zeichen gematcht wird. Folgende Modifikatoren stehen zur Verfügung:
**0 oder 1 Mal: **
? (Beispiel: a?b?c? matcht a, ab, abc, bc, c)
**Genau 1 Mal: **
Kein Modifikator (Standard)
**0 bis ♾️ Mal: **
* (Beispiel: a*bc matcht bc, abc, aaabc)
1 bis ♾️ Mal:
+ (Beispiel: a+bc matcht abc, aaabc, aber nicht bc)
Genau X Mal:
{X} (Beispiel: a{3}bc matcht aaabc, aber nicht aabc, aaaabc)
X bis Y Mal:
{X,Y} (Beispiel: a{2,5}bc matcht aaaaabc, aber nicht abc)
X bis ♾️ Mal:
{X,} (Beispiel: a{2,}bc matcht aaaaaaaabc, aber nicht abc)
Die gleichen Modifikatoren funktionieren auch auf Gruppen (z.B. (abc)+) (siehe unten bei Gruppen).
Benutzerdefinierte Zeichensätze (erstellt mit [ und ])
Du kannst eigene Zeichensätze definieren, indem du die Zeichen ohne Trennzeichen in eckigen Klammern auflistest, z.B. für einen Satz der Zeichen a, b, c und der Zahlen 1, 2, 3 schreibst du [abc123]. Dies wird dann als “ein Zeichen aus diesem Satz” betrachtet, und um mehrere davon zu matchen, braucht man Zeichenmodifikatoren wie [abc123]* oder [abc123]{2,5}.
Du kannst auch ^ am Anfang eines benutzerdefinierten Zeichensatzes verwenden, um anzugeben, dass du jedes Zeichen akzeptierst, außer denen, die du in den Klammern angegeben hast, z.B. [^\\n] um jedes Zeichen außer einem Zeilenumbruch zu akzeptieren.
Bei Zeichen, von denen du weißt, dass sie direkt aufeinanderfolgen – wie Zahlen oder das Alphabet – kannst du auch Bereiche verwenden, indem du ein - dazwischensetzt, z.B. [a-zA-Z0-9].
[abc123]{3,} würde nicht a, b, c, ab matchen, aber 111, abc schon.
Vordefinierte Zeichensätze (\\s, \\S, \\d, \\D, \\w, \\W)
Die folgenden Zeichensätze (vereinfacht) sind bereits vordefiniert und können direkt verwendet werden:
\\sentspricht im Wesentlichen[ \\t\\n], liest sich als “ein beliebiges Leerzeichen”\\Sentspricht im Wesentlichen[^ \\t\\n], liest sich als “ein beliebiges Nicht-Leerzeichen”\\dentspricht im Wesentlichen[0-9], liest sich als “eine beliebige Ziffer”\\Dentspricht im Wesentlichen[^0-9], liest sich als “eine beliebige Nicht-Ziffer”\\wähnlich wie[a-zA-Z_0-9](einschließlich Umlaute usw.), liest sich als “ein beliebiges Wortzeichen”\\Wähnlich wie[^a-zA-Z_0-9], liest sich als “ein beliebiges Nicht-Wortzeichen”
Gruppen (z.B. ( und ), (?<name> und ))
Gruppen kann man sich wie “Wörter” oder “Sätze” vorstellen – sie ändern den Standard-Baustein, der “Zeichen” ist, für jeden Modifikator auf einen Satz von Zeichen, also eine “Gruppe”. Zum Beispiel liest sich abc* als “einmal a, einmal b und beliebig oft c”. Wenn du “beliebig oft abc” schreiben willst, machst du es so: (abc)*. Das abc wird dann als eine Gruppe betrachtet und die Regex würde den gesamten String abcabcabc matchen.
Gruppen erlauben es auch, verschiedene Optionen zur Auswahl anzugeben. Dafür schreibst du eine Gruppe und trennst die verschiedenen Wörter mit einem |, so: (abc|def) – das liest sich als “entweder abc oder def” und würde sowohl 123abc123 als auch 456def456 matchen, aber nicht adbecf.
Diese erfassen bestimmte Teilabschnitte einer Regex und weisen ihnen eine Nummer oder einen Namen zu, der dann im Code oder in Ersetzungs-Templates referenziert werden kann. Typischerweise werden Capture Groups wie in (.*)@(.*).com dann über \\1@\\2.com oder $1@$2.com (je nach Sprache) zurückreferenziert.
Es ist auch möglich, den Gruppen Namen zu geben, z.B. (?<user>.*)@(?<domain>.*).com, um sie dann wie in ${user}@${domain}.com zu referenzieren, aber das sind fortgeschrittene Features, die in verschiedenen Sprachen unterschiedlich implementiert sind (und in manchen fehlen).
Match-Modifikatoren (z.B. \\A, \\z, ^, $, Lookaheads und Lookbehinds)
Standardmäßig wird ein Match für eine Regex wie abc wie eine contains-Methode ausgeführt. Du kannst aber auch angeben, dass der String abc am Anfang oder Ende eines gegebenen Strings oder einer Zeile stehen muss. Zum Beispiel stellt das ^ in ^abc sicher, dass nur Strings matchen, bei denen abc am Anfang einer neuen Zeile steht. Das würde def\\nabc matchen, aber nicht defabc. Das $ in abc$ stellt sicher, dass nach abc ein Zeilenende kommt. Verwende \\A und \\z, um über den gesamten String zu matchen (über mehrere Zeilen hinweg).
Lookaheads und Lookbehinds sind eher ein fortgeschrittenes Thema und besonders dann nützlich, wenn du matchen willst, dass der Anfang oder das Ende deiner Regex NICHT auf eine bestimmte Regex passt. In den meisten Fällen können Regexes mit Lookaheads und Lookbehinds auch mit Capture Groups umgeschrieben werden, also solltest du sie als Capture Groups zu schreiben versuchen und dich nur über Lookarounds informieren, wenn die anderen Optionen nicht funktionieren – denn Lookarounds sind CPU-intensive Operationen und auch etwas eingeschränkt (z.B. unterstützen sie die meisten Modifikatoren nicht).
Hier ist ein guter Ort, um mehr darüber zu erfahren:
Häufige Eigenheiten und Validierung neuer Regexes
Eine häufige Sache, die man beachten sollte: Der . matcht in den meisten Sprachen standardmäßig nicht den Zeilenumbruch. Aber das lässt sich typischerweise mit einer Option aktivieren – in Ruby z.B. durch Angabe von /m am Ende, was für “make dot match newlines” steht.
Beachte auch, dass es in jeder Sprache verschiedene Zeichen gibt, die reserviert sind, je nachdem wie Strings in der jeweiligen Sprache funktionieren. In Ruby z.B. muss / mit \\/ escaped werden, in Swift ist dieses Escaping nicht nötig, aber dafür muss man { und } mit \\{ und \\} escapen. Diese Eigenheiten sind wichtig, wenn man Regexes aus anderen Sprachen kopiert und einfügt.
Generell empfehle ich beim Schreiben einer neuen Regex, eine Website oder ein Tool mit diesen 3 Features zu verwenden:
Eine Option, einen Beispiel-String zum Abgleichen hinzuzufügen.
Ein Regex-Cheat-Sheet direkt auf dem Bildschirm zum Nachschlagen.
Ein Live-Matcher für die Regex, die du schreibst, basierend auf dem gegebenen Beispiel-String.
Die Seite, die ich dafür verwende, ist diese (sie läuft mit Ruby im Hintergrund):
Wie kann ich Regexes heute schon in meinen Projekten nutzen?
Du musst nicht warten, bis sich eine gute Gelegenheit ergibt, Regexes einzusetzen – du kannst deine Projekte einfach mit regulären Ausdrücken linten (inklusive Autokorrektur-Unterstützung) über AnyLint:
github.comFlineDev / AnyLintLint anything by combining the power of scripts & regular expressions

