Regular Expressions

vorige Präsentation: Prototypen + Vererbung | zurück zum Buch-Kapitel [esc] | Nächste Präsentation Mobile

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:

Javascript Code 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.

Javascript Code 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/

RegEx Tester: /hallo/

Alternative

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

/en|sk|zh|us|uk/

RegEx Tester: /en|sk|zh|us|uk/

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$/

RegEx Tester: /^(en|sk|us|uk)$/

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!

RegEx Tester: /hall[oia]/

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]/

RegEx Tester: /[a-z]/

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]/

RegEx Tester: /u[^1234]/

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_]/

RegEx Tester: /u\d/

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+/

RegEx Tester: /\d+/

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

RegEx Tester: /^…$/

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

RegEx Tester: /^\d.\d$/

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?/  

RegEx Tester: /^\d?$/

Gruppieren und Merken

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

/(de|fr)_(DE|CH)/

RegEx Tester: /(de|fr)_(DE|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:

RegEx Tester: /.*/

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.

RegEx Tester: /[^]*_/

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

/o(.*?)o/    
/o(.+?)o/    

RegEx Tester: /.*?/

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

Php Code 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 )){ ...

Php Code Suchen mit RegEx in PHP

preg_match( "/regex/i", "string in dem ich suche") 

Tools

Vertiefung

Merchandize

Regular Expressions

vorige Präsentation: Prototypen + Vererbung | zurück zum Buch-Kapitel [esc] | Nächste Präsentation Mobile

/

#