WWW-Programmierung I, Vorlesung 11

<- Vorlesung 10 Übungen Vorlesung 12 ->


34) Reguläre Ausdrücke

Bei der Programmierung von Web-Seiten - sei es auf der Client-Seite mit JavaScript oder auf der Server-Seite mit Perl oder PHP - ist ein häufig auftretendes Problem das Suchen von "unscharfen" Suchbegriffen in Texten. Zum Beispiel soll ein Benutzer eine Email-Adresse eintippen und das JavaScript-Programm soll testen, ob es sich wirklich um eine Email-Adresse handelt. Oder zwei Wörter - wie Name und Vorname - sollen vertauscht werden. Oder Sie suchen alle Wörter, die mit einem "A" beginnen und mit "ingen" enden, egal wieviele Buchstaben dazwischen liegen. Ein weiteres einfaches Beispiel, das häufig auftritt: Sie wollen, daß in einem String Wörter durch genau ein Leerzeichen getrennt sind, d.h. doppelte oder mehrfach auftretende Leerzeichen in einem String müssen auf ein Leerzeichen reduziert werden.

Zu diesem Zweck gibt es das Konzept der regulären Ausdrücke. Man kann darüber wesentlich mehr sagen, als in diesem Rahmen möglich ist, es gibt Bücher, die sich ausschließlich diesem Thema widmen: Reguläre Ausdrücke (Jeffrey E.F. Friedl), O'Reilly-Verlag.

Nahezu ungewöhnlich ist die Tatsache, dass reguläre Ausdrücke in allen Systemen fast gleich implementiert sind. Sie können das in JavaScript Gelernte genauso in Perl und PHP oder anderen Script-Sprachen anwenden.

Nun aber genug der Vorrede, wir wollen die Sache ausprobieren. In JavaScript gibt es drei Methoden des String-Objects, die mit regulären Ausdrücken arbeiten:

Reguläre Ausdrücke werden immer zwischen zwei "/" eingeschlossen:
	text = "Fischers Fritz fischt frische Fische. Frische Fische fischt Fischers Fritz.";
	erg = text.match(/Fisch/);
	alert(erg);
Damit wird die Zeichenkette Fisch in dem Text gesucht und ausgegeben, falls sie gefunden wurde. Will man alle Vorkommen des gesuchten Ausdrucks ausgeben, so gibt man hinter dem letzten Schrägstrich (/) noch ein g wie "global" an. Damit gibt match() ein Array zurück, falls der Ausdruck mehr als einmal gefunden wird:
	text = "Fischers Fritz fischt frische Fische. Frische Fische fischt Fischers Fritz.";
	erg = text.match(/Fisch/g);
	if (erg) {
		for (i=0;i<erg.length;i++)
			alert(erg[i]);
	}
Normalerweise wird Groß- und Kleinschreibung unterschieden. Soll die Groß-/Kleinschreibung bei der Suche ignoriert werden, so muß hinter dem letzten Schrägstrich noch ein i</tt> wie "ignore" angegeben werden:
	text = "Fischers Fritz fischt frische Fische. Frische Fische fischt Fischers Fritz.";
	erg = text.match(/Fisch/gi);
	if (erg) {
		for (i=0;i<erg.length;i++)
			alert(erg[i]);
	}
Bisher haben wir noch keine spektakulären Ergebnisse erzielt. Nun aber kommen wir zum eigentlichen Potential der regulären Ausdrücke. Wollen wir alle Teilstrings ausgeben, die sowohl aus Fisch oder Frisch bestehen, so müssen wir folgendes tun:
	text = "Fischers Fritz fischt frische Fische. Frische Fische fischt Fischers Fritz.";
	erg = text.match(/Fr*isch/gi);
	if (erg) {
		for (i=0;i<erg.length;i++)
		alert(erg[i]);
	}
Der Stern * bewirkt, daß alle Zeichenketten gefunden werden, die mit F beginnen, danach null oder mehrere Male ein r haben und danach mit isch enden. * bedeutet also, ein Zeichen darf beliebig oft auftreten, muß aber nicht auftreten. Im Gegensatz dazu bedeutet +, ein Zeichen kann beliebig oft, aber mindestens einmal auftreten:
	text = "Fitz Fritz Frritz Frrrritz";
	erg = text.match(/Fr+isch/gi);
	if (erg) {
		for (i=0;i<erg.length;i++)
			alert(erg[i]);
	}
Dies als erster Einstieg. Nun zu den anderen Methoden des String-Objekts:

Hier ist eine kleine Übersicht (aus Selfhtml), was wir abfragen können, die allerdings bei weitem nicht vollständig ist:
^/^aus/ findet "aus" am Anfang des zu durchsuchenden Wertes, also in "aus" und "auserlesen", sofern das die ersten Wörter im Wert sind.
$ /aus$/ findet "aus" am Ende des zu durchsuchenden Wertes, also in "aus" und "Haus", sofern das die letzten Wörter im Wert sind.
* /aus*/ findet "aus", "auss" und "aussssss", also das letzte Zeichen vor dem Stern 0 oder beliebig oft hintereinander wiederholt.
+ /aus+/ findet "auss" und "aussssss", also das letzte Zeichen vor dem Stern mindestens einmal oder beliebig oft hintereinander wiederholt.
. /.aus/ findet "Haus" und "Maus", also ein beliebiges Zeichen an einer bestimmten Stelle.
.+ /.+aus/ findet "Haus" und "Kehraus", also eine beliebige Zeichenfolge an einer bestimmten Stelle. Zusammensetzung aus beliebiges Zeichen und beliebig viele davon.
\b /\baus\b/ findet "aus" als einzelnes Wort. \b bedeutet eine Wortgrenze.
\B /\Baus\B/ findet "aus" nur innerhalb von Wörtern, z.B. in "hausen" oder "Totalausfall". \B bedeutet keine Wortgrenze.
\d /\d.+\B/ findet eine beliebige ganze Zahl. \d bedeutet eine Ziffer (0 bis 9)
\D /\D.+/ findet "-fach" in "3-fach", also keine Ziffer.
\f /\f/ findet ein Seitenvorschubzeichen.
\n /\n/ findet ein Zeilenvorschubzeichen.
\t /\t/ findet ein Tabulatorzeichen.
\s /\s/ findet jede Art von white space, also \f\n\t\v und Leerzeichen.
\S /\S.+/ findet ein beliebiges einzelnes Zeichen, das kein white space ist, also kein \f\n\t\v und kein Leerzeichen.
\w /\w.+/ findet alle alphanumerischen Zeichen und den Unterstrich (typische Bedingung etwa für programmiersprachengerechte selbstvergebene Namen).
\W /\W/ findet ein Zeichen, das nicht alphanumerisch und auch kein Unterstrich ist (typisch zum Suchen nach illegalen Zeichen bei programmiersprachengerechten selbstvergebenen Namen).
\() /(aus)/ findet "aus" und merkt es sich intern. Bis zu 9 solcher Klammern (Merkplätze) sind in einem regulären Ausdruck erlaubt.
Desweiteren kann man mit eckigen Klammern Bereiche angeben:

	text = "Fisch Tisch Misch";
	erg = text.match(/[FT]isch/g);
	if (erg) {
		for (i=0;i<erg.length;i++)
			alert(erg[i]);
		}
Dieses Beispiel wird "Fisch" und "Tisch" ausgeben, d.h. Teilstrings, die mit "F" oder "T" beginnen, und dann "isch" enthalten. Oder man gibt mit "[A-N]" den Bereich der Großbuchstaben von "A" bis"N" an. Groß- und Kleinbuchstaben gibt man mit "[A-Na-n]" an.

Will man ein Zeichen suchen, das eine besondere Bedeutung bei regulären Ausdrücken hat, wie zum Beispiel ".", "*" oder "+", so muß ein Backslash davor gestellt werden: "\.", "\*" und "\+".

Eine Email-Adresse könnte man nun folgendermaßen testen:

	text = "xxx@uni.de yyy.uni.g";
	erg = text.match(/\w+@\w+\.\w+/gi);
	if (erg) {
		for (i=0;i<erg.length;i++)
			alert(erg[i]);
}
Es muß mindestens ein alphanumerisches Zeichen erscheinen, dann das "@"-Zeichen, danach wieder mindestens ein alphanumerisches Zeichen, gefolgt von einem Punkt und mindestens einem weiteren alphanumerischen Zeichen.

Beim Ersetzen von regulären Ausdrücken gibt es weitreichende Möglichkeiten: Zum Beispiel kann man Strings damit vertauschen. Betrachten wir folgendes Beispiel:

	text = "Fritz Fischer";
	ausdruck = /(\w+)\s+(\w+)/;
	ausdruck.exec(text);
	alert( RegExp.$2 + "s " + RegExp.$1);
Die Variable ausdruck enthält einen regulären Ausdruck, der nach einem Wort sucht, gefolgt von white space, danach wieder ein Wort. Jedes dieser Wörter wird mit (\w+) gesucht. Die runden Klammern geben an, daß sich JavaScript die gefundenen Ausdrücke merkt. Mit RegExp.$1 und RegExp.$2 können wir wieder darauf zugreifen. RegExp.$1 enthält das erste gefundene Wort des Ausdrucks, RegExp.$2 das zweite Wort.

Weitere Möglichkeiten:
Will man Alternativen suchen, so verwendet man das Zeichen "|":

	text = "Kaffee mit Milch oder ohne Milch";
	erg = text.match(/mit|ohne/gi);
	if (erg) {
		for (i=0;i<erg.length;i++)
			alert(erg[i]);
	}
Dieses Beispiel sucht Teilstrings, in denen entweder "mit" oder "ohne" enthalten ist. Will man genau angeben, wie oft ein Zeichen vorkommen soll, so gibt man die Zahl in geschweiften Klammern an:
	text = "Schifffahrt";
	erg = text.match(/f{3}/gi);
	if (erg) {
		for (i=0;i<erg.length;i++)
			alert(erg[i]);
	}
Hier bedeutet f{3} den String der aus 3 f hintereinander besteht.


<- Vorlesung 10 Übungen Vorlesung 12 ->

Alfred.Wassermann@uni-bayreuth.de