Regular Expressions sind eine kleine “Programmiersprache in der Programmiersprache”, eine besondere Schreibweise die es erlaubt in Strings nach Mustern zu suchen und Ersetzungen vorzunehmen.

Mit Regular Expressions kann man die Welt retten:

Basierend auf http://xkcd.com/208/ von Randall Munroe - verändert und verwendet unter der CC-BY-NC Lizenz

Regular Expressions sind keine Besonderheit von Javascript. und die meisten Programmiersprachen bieten Regular Expressions an. Egal ob Sie also mit vi, grep, mod_rewrite, Javascript, C++ oder Ruby arbeiten, alles was Sie über Regular Expressions lernen zahlt sich aus!

Auch in Kommandozeilne Befehlen (vi, sed) und Editoren gibt es Regular Expressions, z.B. in Visual Studio Code:

Vielleicht kennst Du Regular Expressions also schon? Wie schätzt Du Dich selbst ein? ⭐? 🌧?

Reguläre Ausdrücke sind ein Konzept aus der Theoretischen Informatik. Diese ursprünglichen regulären Ausdrücke bieten nur 3 Operatoren an. Bei der praktischen Umsetzung wurden mehr und mehr Operatoren hinzugefügt.

Diese Kapitel beschreibt die praktische Umsetzung, und nicht den Begriff aus der theoretischen Informatik.

Regular Expressions nennt man oft auch Muster (en: patterns), man spricht von Mustersuche (en: pattern matching). Wenn ein Muster auf ein Zeichenkette zutrifft sagt man auf Halb-Englisch: “der Pattern matched”.

Verwendung von Regular Expressions

In Javascript gibt es viele verschiedene Schreibweise für Regular Expressions, wir beginnen mit der Methode match des String-Objekts:

match
var s = "begreifen";
if( s.match(/greif/) ) {
  console.log("greif gefunden!");
}

Hier wird im String “begreifen” nach dem String “greif” gesucht. Falls er gefunden wird (ja, wird er), gibt die Match-Methoden einen Wert zurück, der als wahr gilt.

Für diesen einfachsten Fall wird man aber nicht eine RegEx verwenden, sondern indexOf: diese Methode gibt -1 zurück falls der String nicht gefunden wurde, und die Position der Fundstelle anderfalls.

match
var s = "begreifen";
if( s.indexOf('greif') > -1) {
  console.log("greif gefunden!");
}

Zeichenkette

Die einfachste Regular Expression besteht aus einer Zeichenkette, nach der “wörtlich” gesucht werden soll. Falls die Zeichenkette irgendwo im String gefunden wird ist die Suche erfolgreich:

/hallo/
hallo
aber hallo!
die hallobergrenze
halo
Hallo

Alternative

Mit dem senkrechten Strich | kann man Alternative definieren, er entspricht also einem logischen “oder”;

/en|sk|zh|us|uk/
en
sk
uk
ukulele
esuu

Verankern

Mit den Zeichen Zirkumflex ^ und Dollar $ kann die Suche am Anfang bzw. Ende der Strings verankert werden.

/^Am Anfang war/
/dann leben sie noch heute.$/
/^Ganzer String$/
en
sk
uk
ukulele
Maske
schlafen

Achtung: der Zirkumflex ^ hat auch noch andere Bedeutungen (wenn er nicht am Anfang des Patterns steht).

Zeichen-Klassen

Wenn für ein Zeichen im String mehrere Zeichen zur Auswahl stehen fasst man sie in einer “Zeichen-Klasse” (en: “character class”) zusammen:

/[aeiou]/

Achtung! Eine Erwähnung der Zeichenklasse matched genau ein Zeichen im String, nicht mehrere Zeichen!

halli
hallo
halla
halloi
hall
hallö

Zeichen-Klasse mit Zeichenbereich

Mit einem Bindestrich - innerhalb der Klasse kann man einen Bereich von Zeichen angeben, die im Zeichensatz hintereinander stehen.

Die folgenden beiden pattern sind also gleichbedeutend:

/[abcdef]/
/[a-f]/
a
b
c
x
y
z
A
.
%

Komplement der Zeichen-Klasse

Mit dem Zirkumflex ^ kann man das Komplement der Zeichen-Klasse bilden, es werden dann alle Zeichen gematched die nicht in der eckigen Klammer erwähnt werden:

/[^aeiou]/
u1
u2
u3
u4
u5
u6
usa

Achtung: das war die zweite Bedeutung des Zirkumflex ^, wenn er am Anfang der Klasse steht.

Abkürzungen für häufig benutze Zeichenklassen

/\d/       eine Ziffer, entspricht /[0-9]/
/\D/       keine Ziffer, entspricht /[^0-9]/
/\w/       Wort-Zeichen, entspricht /[a-zA-Z0-9_]/
/\W/       kein Wort-Zeichen, entspricht /[^a-zA-Z0-9_]/
u1
u2
u3
u4
u5
u6
u9
usa

Plus-Operator: mindestens einmal, oder mehrmals

Der Plus-Operator erlaubt eine Wiederholung des vorigen Zeichens. Um also "i" und "iiii" und "wir sind die Ritter die ni sagen" zu matchen:

/i+/

Der Operator kann auch auf Zeichenklassen oder Gruppen angewandt werden.

/\d+/
1
12
123

1a
a123

Wenn man sich den Operator als Schleife vorstellt, kann bei jeder “Wiederholung” ein anderes Zeichen aus der Klasse oder eine andere Alternative gewählt werden:

/(do|re|mi)+/

Übung

Eine kurze Übung in TDDbin: Längenangaben finden

Irgend ein Zeichen

Der Punkt . steht für ein beliebiges Zeichen. Achtung, Verwechslungsgefahr: bei Pfadangaben hat das Fragezeichen ? diese Funktion!

/^...$/       genau drei Zeichen
NA
MMT
mmt

___
MMT-B

Ein echter Punkt

Da der Punkt . eine besondere Bedeutung in einer Regular Expression hat stellt sich die Frage: wie erkennt man dann einen echten Punkt? Die Antwort: man escaped die Sonderzeichen von RegEx mit einem Backslash \.

/\./     ein echter punkt
1
1.1
1.2
1.3
2
2.1
2.2
35
35.1

Stern-Operator: beliebig viele

Wir kennen schon den Plus-Operator. Der Stern-Operator erlaubt auch null Wiederholungen.

Der Stern-Operator dient auch der Vervielfältigung: das davor stehende Zeichen kann nullmal, einmal oder mehrmals vorkommen:

/i*/

Fragezeichen-Operator: einmal oder keinmal

Der Frage-Operator erlaubt 0 oder 1 vorkommen des Zeichens.

/\d?/
1
12
123

1a
a1

Gruppieren und Merken

Mit runden Klammern kann man Teile der Regular Expression zusammen fassen:

/(de|fr)_(DE|CH)/
de_DE
fr_DE
de_CH
fr_CH
de_AT
it_CH

Ausserdem stehen die von den Klammern gefundenen Teile des Strings nach der Auswertung zur Verfügung: die Methode match liefert ein Array als Rückgabewert, an der Stelle 0 ist der gesamte gefundene String gespeichert, auf 1, 2, 3 der Reihe nach die gefundnen Gruppen:

locale = "de_CH";
if( match = locale.match(/(de|fr)_(DE|CH)/ ) {
  console.log("gesamt:  " + match[0]);
  console.log("sprache: " + match[1]);
  console.log("land:    " + match[2]);
}

Übung

Eine längere Übung in TDDbin: Längenangaben finden

Operatoren sind gierig

Die Operatoren * und + versuchen immer möglichst lange Strings zu erfassen.

/o.*o/    möglichst viele Zeichen zwischen o und o

Diese Eigenschaft sieht man sehr gut in folgendem Beispiel:

zeugs und _bla_ und zeugs
_bla_ und zeugs
zeugs und _bla_
_bla_ und _bla_ und noch mehr _bla_

Wie kann man das gierige Verhalten der Operatoren umgehen?

Die “altmodisch” Methode ist eine Komplement-Klasse:

/o[^o]*o/

Hier wird ein erstes o gematched, dann kommen (null bis viele) zeichen die kein o sind, und dann ein zweites o. Damit ist der Pattern beendet, auch wenn es später im String noch weitere o’s geben würde.

zeugs und _bla_ und zeugs
_bla_ und zeugs
zeugs und _bla_
_bla_ und _bla_ und noch mehr _bla_

In modernen RegEx Engines gibt es nicht-gierige Varianten der Operatoren: ein Fragezeichen wird nachgestellt

/o(.*?)o/
/o(.+?)o/
zeugs und _bla_ und zeugs
_bla_ und zeugs
zeugs und _bla_
_bla_ und _bla_ und noch mehr _bla_

Mehrmals suchen

Alle bisher geschriebenen Patterns werden mit der Funktion match einmal gesucht. Wenn ich in einem string aber mehrere Vorkommen finden will, braucht es das global-Flag:

m = "Nana nana nana nana batman".match(/na/);
// findet das erste na
m = "Nana nana nana nana batman".match(/na/g);
// findet alle 7 nax

Warnhinweis: Was RegEx nicht kann

Mit dem letzten Beispiel könnte man nun in Versuchung kommen verschachtelte Ausdrücke wie HTML, XML, mathematische Ausdrücke, Programmiersprachen mit Hilfe von RegEx zu parsen.

Das funktioniert aber nicht. Das müssten Sie an dieser Stelle einfach mal glauben - den Beweis überlassen wir der “Theoretischen Informatik”.

Die Frage ob das geht kommt immer wieder auf StackOverflow, hier ist die Standard Antwort.

Wenn Sie also in Javascript oder PHP HTML oder XML parsen wollen, dann verwenden Sie dafür einen der vielen fertigen Parser.

Wenn Sie aus einem längeren HTML-Dokument “unverschachtelte” Teile wie z.B. <img> Tags herausholen wollen, dann könnsn Sie das mit Regular Expressions machen. Aber nichts komplizierteres.

Ersetzen mit Regular Expression in Javascript

Ein häufiger Anwendungsfall von RegEx ist “Suchen und Ersetzen”

s = "Voldemort hat keine Nase";
s.replace(/Voldemort/,
          "Er, dessen Name nicht genannt werden darf,");

Es erfolgt nur eine einzige Ersetzung. Mit dem Modifikator g am Ende der RegEx kann man alle Ersetzungen durchführen:

s = "Voldemort hat keine Nase. Voldemort ist verschwunden.";
s.replace(/Voldemort/g,
          "Er, dessen Name nicht genannt werden darf,");

Achtung, auch hier kommt man bald an die Grenzen von Regular Expressions:

s = "Harry greift Voldemort an. Voldemorts Zauberstab bricht.";
s.replace(/Voldemort/g,
          "Er, dessen Name nicht genannt werden darf,");

Dass im zweiten Satz ein anderer Fall notwendig wäre, und wie man das dann richtige formuliert, kann eine Regular Expression nicht erkennen. Dafür gibt es eine eigene Wissenschaft: die Computer-Linguistik beschäftigt sich mit der Analyse, Synthese und Übersetzung von natürlichen Sprachen.

RegEx in PHP

RegEx Beispiel-Code aus Wordpress Plugins
# Beispiel Link Suchen
if (preg_match('/^(http)(s?)(:)\/\//',$linky)){ ...

# Beispiel Sprache-Code

if (preg_match ( "/en|sk|zh|us|uk/", \$locale_code )){ ...
Suchen mit RegEx in PHP
preg_match( "/regex/i", "string in dem ich suche")

Tools

Vertiefung

Merchandize