Reguläre Ausdrücke mit JavaScript

Veröffentlicht am Montag, 31. Dezember 2007, von infinity auf Alphane Moon

Regex oder reguläre Ausdrücke sind ein kryptisches Thema. Die voodooartige Syntax macht den Einstieg schwer und technische Referenzen sind oft nicht besonders fesselnd geschrieben. Trotzdem ist es nützlich sich einen Überblick über reguläre Ausdrücke zu verschaffen, weil sie einem in verschiedenen Situtionen begegnen können.

Es ist nicht wirklich so schwer wie es aussieht, wenn man mit einigen einfachen Beispielen beginnt. Deshalb: Keine Panik! Wir werden das schon hinkriegen.

Zum Lernen von regulären Ausdrücken ist JavaScript gut geeignet, denn man braucht nur einen Browser und einen Texteditor. Es gibt zwar bei der Deklaration von regulären Ausdrücken in JavaScript ein paar kleine Unterschiede zu anderen Programmiersprachen (z.B. Perl) und es fehlen einige Optionen, aber das Prinzip ist das gleiche. Die Kombination von JavaScript mit regulären Ausdrücken kann man praktisch einsetzen, um Formulareingaben zu validieren.

Zum Ausprobieren der Beispiele genügt ein kleines XHTML-Grundgerüst:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<title>Reguläre Ausdrücke mit JavaScript</title>
</head>

  <body>
   <script type="text/javascript">
     //<![CDATA[
       … JavaScript-Code …
     //]]>
   </script>
  </body>
 </html>

Der Code der einzelnen Beispiele ist im CDATA-Container an der Stelle „… JavaScript-Code …“ einzusetzen. Alle Beispiele wurden mit Internet Explorer 7, Opera, Safari und Firefox getestet.

Bemerkung: einige Beispiele benutzen document.write() bzw. document.writeln(). Dies funktioniert nur, wenn die Dateien als text/html behandelt werden. In XHTML, das als application/xhtml+xml ausgeliefert wird, kann man für Zugriffe auf Seitenelemente Methoden des DOM nutzen.

Reguläre Ausdrücke erzeugen

Es gibt zwei Wege um in JavaScript einen regulären Ausdruck zu definieren: direkt über das Objekt RegExp von JavaScript und seinen Konstruktor oder mit einem Literal.

(i) explizit mit dem Konstruktor des RegExp-Objekts:

var searchPattern = new RegExp("Muster");

Es können einfache oder doppelte Anführungszeichen verwendet werden.

(ii) Literale Regex-Syntax:

var searchPattern = /Muster/;

Dort, wo bis jetzt nur „Muster“ steht (harmlos), kommen dann die gefürchteten Zeichenketten hin. In den ersten Beispielen werden von den „schwierigen“ Zeichen nur der Punkt, das Caret und das Plus verwendet. Eine ausführlichere Übersicht über die Zeichen kommt weiter unten.

Ein paar kurze Beispiele zur Eingewöhnung:

var searchPattern = /.abe/;

Dieses Muster passt auf alle Zeichenketten, die mit einem beliebigen Zeichen beginnen, gefolgt von der Zeichenkette „abe“. Zum Beispiel wäre „habe“ ein Treffer, auch „Gabe“ passt.

var searchPattern = /^Dieser/;

Die Zeichenkette „Dieser“ muss am Anfang des Strings stehen, dann passt dieses Muster.

var searchPattern = /t+/;

Dieses Muster passt auf alle Zeichenketten, die aus einem oder mehreren „t“ bestehen.

var searchPattern = /.as+e/;

Dieses Muster passt auf alle Zeichenketten, die mit einem beliebigen Zeichen beginnen, gefolgt von einem „a“, gefolgt von mindestens einem „s“ und einem „e“. „Hase“ ist ein Treffer, aber auch „Nase“ oder „Tasse“.

Methoden von RegExp: test und exec

Das Objekt RegExp hat zwei eigene Methoden: test und exec.

test

Die Methode test prüft, ob ein übergebener String zum regulären Ausdruck passt. Dabei wird auch Groß- und Kleinschreibung berücksichtigt. Bei einer Übereinstimmung ist der Rückgabewert true, ansonsten false.

Ein Beispiel dazu:

var regExpress = /Beispiel/;

var str = "Beispiel";
if (regExpress.test(str)) {
   alert("Hurra, Das Muster passt!");
 }
  else {
   alert("Nein, das Muster passt nicht.");
 }

Hier passiert Folgendes: ein regulärer Ausdruck (regExpress) und ein String (str) werden erzeugt. Im if-else-Teil wird dann zuerst geprüft, ob regExpress.test(str) den Wert true zurückgibt. Je nachdem wird entweder der if-Zweig oder der else-Zweig ausgeführt.

exec

Die Methode exec des RegExp-Objekts erwartet einen Text oder String als Eingabe. Der Text wird auf das Muster durchsucht und eine passende Zeichenkette wird in einem Array gespeichert. Der Rückgabewert von exec ist entweder dieses Array, wenn das Muster mindestens einmal gefunden wurde, oder 0 (Null) bei erfolgloser Suche. Wenn eine zum regulären Ausdruck passende Zeichenkette gefunden wurde, befindet sie sich an der Position 0 des Arrays.

Ein Beispiel zeigt exec in Aktion:

var searchPattern = /.as+e/g;
var str = "Hase und Vase, Nase und Tasse.";

var newArray = searchPattern.exec(str);

while (newArray) {
  document.writeln("<p>" + newArray[0] + "</p>");
  newArray = searchPattern.exec(str);
 }

Das Muster /.as+e/g; passt auf alle Zeichenketten, die mit irgendeinem Zeichen beginnen (der Punkt „.“), gefolgt von einem „a“, gefolgt von einem oder mehreren „s“ (Plus „+“ bedeutet eines oder mehr), gefolgt von einem „e“. Wir schauen uns diese Syntax gleich noch genauer an.

Im String „Hase und Vase, Nase und Tasse.“ werden also die Zeichenketten „Hase“, „Vase“, „Nase“ und „Tasse“ gefunden.

Das kleine „g“ ist das Global-Flag. Es sorgt dafür, dass der gesamte String durchsucht wird (global search). Wenn das Flag g gesetzt ist, wird die Eigenschaft lastIndex des regulären Ausdrucks nach jedem Aufruf von exec auf die Zeichenposition hinter der passenden Zeichenkette gesetzt, solange bis der ganze String durchsucht wurde.

Wenn in diesem Beispiel das Global-Flag nicht gesetzt ist, beginnt die Suche bei jedem Aufruf von exec in der while-Schleife an der Position 0. Wir haben eine Endlosschleife (Pfui!).

Nach einer kleinen Veränderung des Beispiels wird lastIndex mit ausgegeben:

var searchPattern = /.as+e/g;
var str = "Hase und Vase, Nase und Tasse.";
var newArray = searchPattern.exec(str);

while (newArray) {
  document.writeln("<p>" + newArray[0] + " " + searchPattern.lastIndex + "</p>");
  newArray = searchPattern.exec(str);
 }

Wenn alles geklappt hat, lesen wir:

Hase 4

Vase 13

Nase 19

Tasse 29

Das Zeichen auf der Position 0 ist das „H“, „a“ ist auf 1, „s“ auf 2 und „e“ auf 3. lastIndex hat nach dem ersten Aufruf von exec den Wert 4, das ist die Position des Leerzeichens vor „und“.

Flags oder Modus-Modifikatoren

Es gibt drei Flags oder Modus-Modifikatoren, die man zusätzlich angeben kann:

Es können mehrere oder alle Flags gleichzeitig gesetzt werden.

Wenn der reguläre Ausdruck mit dem Konstruktor des RegExp-Objekts erzeugt wird, werden die Flags auf folgende Weise gesetzt:

var searchPattern = new RegExp("Muster", "g");
var anotherPattern = new RegExp("example", "i");
var myPattern = new RegExp(".as+e", "im");

Bei Verwendung der literalen Regex-Syntax funktioniert es so:

var searchPattern = /Muster/g;
var anotherPattern = /example/i;
var myPattern = /.as+e/im;

Beim Setzen mehrerer Flags spielt die genaue Reihenfolge der Flags keine Rolle: im ist das Gleiche wie mi.

Ignore Case

Das Flag i bewirkt, dass die Groß- und Kleinschreibung ignoriert wird.

Im ersten Beispiel zur Methode test hatte wir gesehen, dass die Groß- und Kleinschreibung berücksichtigt wurde. Hier ist eine veränderte Version des Beispiels, die das Flag i benutzt:

var regExpress = /beispiel/i;
var str = "Beispiel";

if (regExpress.test(str)) {
  alert("Hurra, Das Muster passt!");
 }
  else {
   alert("Nein, das Muster passt nicht.");
 }

„Beispiel“, „beiSPIel“ und „beispiel“ werden von /beispiel/i gefunden.

Global Search

Das Global-Flag g hatten wir schon im Beispiel zur Methode exec gesehen. Es bedeutet, dass im gesamten String nach einem Muster gesucht werden soll. Wir werden das Global-Flag später bei der replace-Methode wieder verwenden.

Multiline Input

Mit dem Multiline-Flag m kann man über mehrere Zeilen hinweg suchen. Schauen wir uns das an einem Beispiel an:

var searchPattern = /^.as+e/ig;
var str = "Hase in der \nVASE ?!";

var newArray = searchPattern.exec(str);

while (newArray) {
  document.writeln("<p>" + newArray[0] + " " + searchPattern.lastIndex + "</p>");
  newArray = searchPattern.exec(str);
 }

var multilinePattern = /^.as+e/igm;
var multilineArray = multilinePattern.exec(str);

while (multilineArray) {
  document.writeln("<p>" + multilineArray[0] + " " + multilinePattern.lastIndex + "</p>");
  multilineArray = multilinePattern.exec(str);
 }

In diesem Beispiel wird einmal mit und einmal ohne das Flag m gesucht. In beiden Fällen sind zusätzlich die Flags i und g gesetzt.

Das Muster ^.as+e verwendet das Caret ^ und passt ohne Multiline-Modus nur auf Zeichenketten, die am Anfang eines Strings stehen. Der String str enthält einen Zeilenumbruch \n, also zwei Zeilen. Der „Hase“ am Anfang der ersten Zeile wird in beiden Fällen gefunden, aber die „VASE“ in der zweiten Zeile wird nur bei gesetztem Multiline-Flag gefunden.

Wenn nichts schief gelaufen ist, sehen wir also:

Hase 4

Hase 4

VASE 17

Im Multiline-Modus bedeutet das Caret ^, dass die nachfolgenden Zeichen am Anfang einer Zeile, d.h. am Anfang des Strings oder direkt nach einem Zeilenumbruch stehen müssen.

Regex-Syntax in JavaScript

Als nächstes wollen wir einen Blick auf die Regex-Syntax und die Metazeichen werfen. Der Punkt, das Caret und das Plus sind uns bereits begegnet.

Zeichenklassen und vordefinierte Klassen

Mit den eckigen Klammern […] kann man eine Menge von Zeichen festlegen. Zum Beispiel passt [a-k] auf irgendein Zeichen von „a“ bis „k“. Man kann mehrere Folgen von Zeichen in einer Klasse zusammenfassen: [a-kL-Z] passt auf irgendein Zeichen, das aus dem Bereich von „a“ bis „k“ oder aus dem Bereich von „L“ bis „Z“ stammt. Das Caret kann man in eckigen Klammern einsetzen, um festzulegen, dass das Zeichen eben nicht aus einem bestimmten Bereich stammen soll. Einige Klassen sind schon vordefiniert.

[…] – Irgendein Zeichen aus einer Liste
[^…] – Irgendein Zeichen, das nicht in dieser Liste enthalten ist

[a-k] – Irgendein Zeichen aus der Menge von „a“ bis „k“
[^a-k] – Irgendein Zeichen, das sich nicht in dieser Liste befindet

. – Irgendein Zeichen (aber kein Trennzeichen)

\d – Irgendeine Ziffer, das Gleiche wie [0-9]
\D – Das gleiche wie [^0-9]
\s – Irgendein Whitespace-Zeichen (z.B. Leerzeichen
     oder ein Zeilenumbruch)
\S – Irgendein Zeichen, das kein Whitespace-Zeichen ist
\w – Irgendein Zeichen aus [a-zA-Z0-9], also ein
     Groß- oder Kleinbuchstabe oder eine Ziffer
\W – Das Gleiche wie [^a-zA-Z0-9]

Achtung: Wenn man reguläre Ausdrücke mit dem Konstruktor des RegExp-Objekts definiert, muss man bei der Verwendung der vordefinierten Klassen mit dem Backslash \ einen weiteren Backslash als Escape-Zeichen verwenden!

var searchPattern = new RegExp("H\\wse", "g");
var anotherPattern = /H\wse/g;

Das folgende Beispiel benutzt die Klasse [ao], die nur „a“ und „o“ enthält:

var searchPattern = /H[ao]se/g;
var str = "Hase und Hose, Nase und Tasse.";

var newArray = searchPattern.exec(str);

while (newArray) {
  document.writeln("<p>" + newArray[0] + "</p>");
  newArray = searchPattern.exec(str);
 }

H[ao]se passt auf „Hase“ und „Hose“.

var searchPattern = /H\wse/g;
var str = "Hase und Hose, H4se und Hyse.";

var newArray = searchPattern.exec(str);

while (newArray) {
  document.writeln("<p>" + newArray[0] + "</p>");
  newArray = searchPattern.exec(str);
 }

H\wse passt auf „Hase“, „Hose“, „H4se“ und „Hyse“, denn \w steht ja für [a-zA-Z0-9].

Die speziellen Zeichen * + ?

Die Zeichen *, + und ? werden einem Zeichen nachgestellt.

* – null Mal oder häufiger
+ – einmal oder häufiger
? – null Mal oder einmal

Es gibt einige interessante Kombinationen:

.* – Greedy Match
.*? – Lazy Star
?? – null Mal oder einmal, aber so wenig wie möglich
+? – einmal oder mehrmals, aber so wenig wie möglich

Betrachten wir ein paar Beispiele:

var greedyPattern = /a.*a/g;
var greedyString = "alibaba";
var newArray = greedyPattern.exec(greedyString);

while (newArray) {
  document.writeln("<p>" + newArray[0] + "</p>");
  newArray = greedyPattern.exec(greedyString);
 }

Dieses Bespiel verwendet Greedy Match, gefunden wird ein „a“ gefolgt von beliebigen Zeichen bis zum letzten Vorkommen des Zeichen „a“: „alibaba“.

var lazyPattern = /a.*?a/g;
var lazyString = "alibaba";
var newArray = lazyPattern.exec(lazyString);

while (newArray) {
  document.writeln("<p>" + newArray[0] + "</p>");
  newArray = lazyPattern.exec(lazyString);
 }

In diesem Beispiel mit dem Lazy Star ist schon bei „aliba“ Schluß.

regex-Methoden des String-Objekts

Vorher haben wir nur mit den Methoden des RegExp-Objekts gearbeitet. Die Methoden des String-Objekts von JavaScript sind ebenfalls sehr nützlich und werden gerne verwendet.

Was ist ein String-Objekt? Gemeint ist das Wrapper-Objekt des primitiven Datentyps string.

Das String-Objekt besitzt vier interessante Methoden, die wir uns näher anschauen werden:

replace

Die replace-Methode des String-Objekts sucht das Muster im String und ersetzt passende Substrings durch einen Ersetzungsstring. Wenn das Global-Flag gesetzt ist werden alle gefundenen Substrings ersetzt.

Dieses Beispiel ersetzt alle „a“ durch „aktschuf“ und alle „e“ durch „üftal“:

var regA = /a/g;
var regB = /e/g;
var str = "Ersetze alle a durch aktschuf und alle e durch \u00fcftal.";

document.writeln("<p>" + str + "</p>");
str = str.replace(regA, "aktschuf");
document.writeln("<p>" + str + "</p>");
str = str.replace(regB, "\u00fcftal");
document.writeln("<p>" + str + "</p>");

match

Die Methode match durchsucht einen String auf ein Muster. Der Rückgabewert ist entweder ein Array oder -1 (kein Treffer). Wie das Beispiel zeigt, enthält das Element 0 des Arrays den ersten vollständigen Treffer. Hier wird match bei gesetztem Global-Flag ausgeführt, deshalb enthält das Array alle weiteren Treffer.

var regTreffer = /tr.ff/ig;
var str = "Treffer und Triff und Troff";

var theArray = (str.match(regTreffer));
for (i=0; i<theArray.length; i++) {
  document.writeln("<p>" + theArray[i] + "</p>");
 }

search

Die Methode search durchsucht einen String auf ein Muster. Wenn es einen Treffer gibt, gibt die Methode die Position des ersten Treffers zurück. Wenn es keinen Treffer gibt, wird -1 zurückgegeben. Schauen wir uns ein Beispiel an:

var searchPattern = /hase/i;
var str = "Schneehase";

alert(str.search(searchPattern));

Es wird 6 angezeigt, denn die Positionen 0 bis 5 werden von „Schnee“ belegt. Der Substring „hase“ beginnt mit dem „h“ auf Position 6.

split

Mit der Methode split kann man einen String an Trennzeichen, die durch einen regulären Ausdruck gegeben sind, in Substrings zerlegen. Der Rückgabewert von replace ist ein Array, das diese Substrings enthält.

var regEx = /e+/i;
var str = "Der Schneeleopard hat den Polarfuchs gefressen.";

var theArray = (str.split(regEx));
for (i=0; i<theArray.length; i++) {
  document.writeln("<p>" + theArray[i] + "</p>");
 }

Hier dienen Zeichenketten aus einem oder mehrere „e“ als Trennzeichen. Die Trennzeichen werden dabei entfernt.

Man kann auch ein Limit festlegen bis zu welchem Substring gesplittet werden soll:

var regEx = /e+/i;
var str = "Der Schneeleopard hat den Polarfuchs gefressen.";

var theArray = (str.split(regEx, 3));
for (i=0; i<theArray.length; i++) {
  document.writeln("<p>" + theArray[i] + "</p>");
 }

Bei diesem zweiten Beispiel habe ich das Limit auf 3 gesetzt, es werden die ersten drei Substrings gefunden.

Die split-Methode ist nützlich, wenn man mit CSV-Daten arbeiten will. Die Verwendung von regulären Ausdrücken gibt einem aber sehr viel mehr Freiheit bei der Wahl der Trennzeichen oder Trennmuster.

Ende

Damit sind wir am Ende dieses kleinen Ausflugs durch die regulären Ausdrücke in JavaScript angekommen. Es gibt noch sehr viel mehr zu erwähnen (z.B. Capture-Gruppen). Vielleicht schreibe ich irgendwann noch einen zweiten Teil über dieses Thema. Ich hoffe, dass es Spaß gemacht hat.

Ressourcen

all content copyright © 2007-2010 Alphane Moon