Benutzer:Cinematic/tutorial regex

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Einführung in reguläre Ausdrücke für absolute Neueinsteiger

Nicht weglaufen! Reguläre Ausdrücke können einfacher sein als sie oft erklärt werden. Und es geht besser als nur sturre Regeln zu lesen. :-)

Einführung

Bisher konnte man bei der Prüfung von Eingaben des Besuchers auf vordefinierte Funktionen der verschiedenen Programmiersprachen zugreifen, die man für den eigenen Bedarf genutzt hat bzw. in Verbindung mit anderen Funktionen anpassen konnte. So hat man etwa in Javascript um eine Eingabe zu prüfen, ob diese eine Zahl ist die Funktion isNaN(String) verwendet und seine Länge mit length geprüft. Geht es aber um die Syntax einer e-Mail Adresse, wird es auf dem Standardweg schon schwieriger. Bei der Überprüfung von Formulardaten gibt es zwar die Möglichkeit diese anhand von bestimmten Zeichen mit einer Schleife zu durchlaufen, allerdings ergeben sich dabei Einschränkungen wie wann ein Zeichen vorkommen kann, die diese Alternative dazu nicht hat: Reguläre Ausdrücke. Wer reguläre Ausdrücke beherrscht, kann dieses wissen nicht nur bei Javascript anwenden, sondern auch in anderen Programmiersprachen wie zum Beispiel Perl, PHP oder bei der URL Manipulation mod_rewrite, die schöne URLs ermöglicht. Deshalb wird bei diesem Tutorial ganz bewusst darauf verzichtet, ersteinmal Programmcode zu nutzen. Für den Menschen jedenfalls, der reguläre Ausdrücke nutzt, ist es ein wahrer Segen und wird sehr vieles auf dem weiteren Weg der Programmierung vereinfachen. Und entgegen aller Behauptungen es sei schwer: Das ist ein irrtum den sich nur viele einreden versuchen. Es sieht nur ersteinmal ungewohnt aus.

Die Arbeit von regulären Ausdrücken ist simpel: Wir besitzen eine Zeichenkette wie zum Beispiel "Reguläre Ausdrücke sind eine tolle Sache." und erstellen dann ein (Such-) Muster, das wir auf diese Zeichenkette anwenden. Ein Beispiel dieses Musters kann sein, dass ein Wort eine bestimmte Mindest- oder Maximallänge besitzen soll, am Ende des Satzes ein Punkt stehen muss oder ob sich unerlaubte Sonderzeichen in dem Satz befinden wie ä oder ü, da man zum Beispiel eine englischsprachige Webseite betreibt wo auch nur Zeichen des englischen Alphabets vorkommen dürfen. Also geradezu eine Musterlösung – und das ist die Aufgabe von regulärn Ausdrücken zu prüfen, ob der Benutzer eine Musterlösung eingetippt hat.

Im Web hat sich die Abkürzung RegEx etabliert, welche von der englischen Schreibweise "Regular Expressions" herrührt. Wird von einem pattern gesprochen ist damit das Muster gemeint und wenn man von einen subject spricht, zu Deutsch "Thema", ist die Zeichenkette (String) gemeint, die überprüft werden soll. Um die Arbeit mit den regulären Ausdrücken zu vereinfachen, gibt es im Web Programme, mit denen man seine regulären Ausdrücke überprüfen kann. Einige sind komplex und können sehr hilfreich sein, andere sind sehr einfach und reichen in der Regel für denjenigen, der die Syntax verstanden hat vollkommen aus. Für den Anfang geben wir ein einfaches Hilfsmittel zur Hand: Ein RegEx Prüfer – Das Programm erklärt sich mit den vorher vorgestellten Begriffen von selbst und mit den Beispielen die kommen werden, kann dann entsprechend getestet werden.


Kapitel 1: Erste Grundlagen

Fangen wir da an, wo einem zum ersten Mal etwas richtiges beigebracht wurde: In der Schule. Hier geht es weiter, nur wird man für sein eigenenes Engagement nicht benotet. Aber wir wollen trotzdem über Noten sprechen und besonders interessant sind für unseren Anfang die numerischen Schulnoten. Auch wenn diese uns in der Schule so manchen Tag vermiest haben, eignen sie sich hervorragend um mit regulären Ausdrücken einzusteigen. Ein Beispiel anhand einer Zeichenkette:

"Ich habe in der Klassenarbeit eine 5 geschrieben." 

Dieser Satz ist ersteinmal eine Aussage. Wir wissen, dass, nennen wir ihn Karl, nichts schlechteres als eine 6 und nichts besseres als eine 1 schreiben kann. Daher ist unseres Suchmuster ersteinmal simpel und das sieht bei regulären Ausdrücken so aus

[123456]

Wenn Karl jetzt seiner Mutter sagt er hat eine 5 geschrieben, dann weiß sie aus ihrer Kindheit heraus: Eine Note kann aus den Zahlen 1 bis 6 bestehen. In diesem Falle wird die Zahl 5 in das Suchmuster [123456] passen, also hat Karl die Wahrheit gesagt und bekommt ersteinmal als Bestätigung ein wahr (true) zurück. Anders wäre es gewesen, wenn Karl gesagt hätte, er hat eine 0 geschrieben. Dann wird er sich vielleicht selbst damit vertauscht haben, aber seine Mutter wird wissen, dass das nicht richtig sein kann und wird ihm sagen, dass das so nicht stimmen kann, er also falsch (false) liegt. Es ist nur noch nicht bekannt, ob Karl seine Mutter nur schocken wollte und ihr danach die glatte 1 zeigt.

Sehen wir uns einmal die Denkweise seiner Mutter genauer an und halten ihren Kopf mal eben in rechteckigen Klammern fest: Alles was in ihrem Kopf vorkam war die Zeichenkette 123456. Theoretisch gesehen sind das ersteinmal einhunderttausend-dreiundzwanzigtausend-vierhundert-sechsundfünfzig. Die eckigen Klammern sorgen aber dafür, dass jedes Zeichen dazwischen einzeln interpretiert wird. Werte in eckigen Klammern nennt man Zeichenklassen. Demnach ist die Reihenfolge wie die Zeichen dazwischen angeordnet sind, vollkommen egal. Der reguläre Ausdruck könnte demnach auch [654321], [642531] oder [135246] sein. Im Prinzip ergibt das 6² = 36 Möglichkeiten, das Suchmuster für das Notensystem anzulegen. Eine einfachere und kürzere Möglichkeit das zu schreiben, ist diese [1-6] oder diese [6-1] Schreibweise. Damit spart man sich jedes Zeichen einzeln einzusetzen. Was wir jetzt zusätzlich gelernt haben, ist für später wichtig: Die rechteckigen Klammern [ und ] sowie der Bindestrich – haben eine Sonderfunktion in den regulären Ausdrücken. Letzteres Sonderzeichen übernimmt wie wir es auch aus dem Alltag kennen, die Funktion des Wortes "bis".

War das bisher nicht einfach? Gut, das war es. Aber leider ist unsere herangehensweise etwas unsauber ausgeführt worden, denn wir haben ja nur geprüft, ob eine Zahl 1 bis 6 in der Zeichenkette, also der Aussage von Karl vorkommt. Wenn Karl jetzt folgendes zu seiner Mutter sagt:

"Ich habe eine 5 geschrieben und habe mich danach die restlichen 7 Schulstunden nicht mehr konzentieren können." 

Dann werden wir Mitleid mit ihm haben, aber seine Mutter bekommt damit ein ernsthaftes Problem, weil nur die eckigen Klammern, die ihren Kopf zusammenhalten nicht mehr ausreichen. Der Satz kann jetzt noch korrekt sein, aber sobald wir die Zahlen einfach vertauschen, dann kommt es zu einem Dilemma, weil keiner seiner Mutter beigebracht hat in welchem Kontext (Zusammenhang) die Zahl nur vorkommen darf. Sie ist also aufgeschmissen und denkt sich: Solange eine der Zahlen 1 bis 6 vorkommt, wird es schon stimmen. Das impliziert aber auch wieder ein weiteres Problem für die Mutter. Was ist wenn aus der 5 eine 55, 51 oder eine 57 wird? Auch hier gilt: Solange eine der ihren bekannten Zahlen vorkommt, kann Karl auch eine 666 geschrieben haben und sie würde es akzeptieren, und das ist schon sehr teuflisch.

Wir werden uns damit noch auseinandersetzen, aber ersteinmal müssen wir uns eines verdeutlichen: Karl ist ein Besucher unserer Webseite der uns gewollt oder ungewollt falsche Informationen in einem Forumlar übermitteln wird. Sei es bei der Postleitzahl Buchstaben einzubauen oder den Straßennamen mit Sonderzeichen zu schmücken damit es schöner aussieht. Wir schlüpfen hingegen in die Rolle der Mutter und unsere Aufgabe ist es, dass egal welcher Karl was auch immer tut, zu überprüfen, ob er auch artig ist und sich an die gewünschten Regeln hält. Wir stellen also die Regeln auf und überwachen sie automatisch. Und für diese Aufgabe, helfen uns die regulären Ausdrücke.

In der Praxis sieht das meistens natürlich ganz anders aus. Der Besucher bekommt von uns ein Eingabefeld vorgegeben und beschreibend daneben steht, dass man seine Note eintragen soll. Das Gespräch das Karl also mit seiner Mutter führt, wird außen vor gelassen und wir die Mutter, schauen uns allein den angegebenen Wert an, den Karl jetzt abgeschickt hat. Demnach haben wir nur die Eingabe zu prüfen und nicht mehr direkt in welchem Kontext sie steht, den kennen wir ja, weil er neben dem Eingabefeld steht.

<html>
 <head>
   <title>Praxisbeispiel auf einer Webseite zur Abfrage von Benutzereingaben</title>
 </head>
 <body>
   Peter, sag mir bitte deine <input type="text" placeholder="Note">
   Oder stinkformal:
   Tragen Sie bitte ihre Note in das folgende Feld ein: <input type="text">
 </body>
</html>

Aber jetzt ist es wichtig zu wissen, was wir überhaupt prüfen und machen uns daher Gedanken was erlaubt sein soll und was nicht. Nehmen wir die Note genauer unter die Lupe: Sie muss aus den Zahlen 1 bis 6 bestehen, gleichzeitig darf die Zahl die eingegeben wird nicht mehr als aus einem Zeichen bestehen, sodass Eingaben wie 22 oder 333 nicht möglich sind. Außerdem sollten wir bedenken, dass es auch eine Auf- und Abwertung der Note gibt wie 1+ oder eine 4-. Das gilt es jetzt zu prüfen und auch hier müssen wir jetzt aufpassen: Die Zahl muss vor dem + und - stehen. Natürlich wäre das jetzt genauso "einfach" durch ein Array zu lösen, bei dem wir überprüfen ob einer der Werte mit dem eingegebenen Wert übereinstimmt. Reguläre Ausdrücke entfalten aber ihre Stärke besonders dann, wenn besonders viele Zeichen unbekannt sind wie bei einer e-Mail Adresse oder einem Straßennamen, wo wir wissen, dass jeder verschiedene Besucher einen anderen Wert eingeben kann.

Ein Beispiel, wie das mit einem Array gelöst wird, bei dem man anschließend in einer for-Schleife jeden Wert durchgeht und prüft, ob dieser mit einem davon übereinstimmt (Syntax Javascript):

var Noten = new array("1+","1","1-","2+","2","2-","3+","3","3-","4+","4","4-","5+","5","5-","6+","6");


Widmen wir uns aber zu Lernzwecken weiterhin der Notenprüfung mit regulären Ausdrücken zu und schauen zuversichtlich entgegen, dass das Verständnis zum Thema RegEx sehr gut (1+) wird. Fangen wir wieder mit dem an, was wir wissen und begrenzen die Zahl auf die Länge von einem Zeichen mit dem Quantifizierer { }:

[123456]{1} oder [1-6] {1}

Der Wert in der geschweifen Klammer gibt die genaue Anzahl des einen Zeichens an, das vor ihm steht. Da jedoch die 1 bis 6 in Klammern steht, kann ein zufälliges Zeichen davon einmal vorkommen. Anders sieht es so aus, wenn man schreiben würde:

1{1}

In diesem Falle müsste genau einmal die 1 in der Eingabe vorkommen, Karl dürfte also nur sehr gute Noten haben. Schreiben wir hingegen:

5{4}

Muss die 5 genau 4 mal hintereinander vorkommen damit der reguläre Ausdruck auf die Zeichenkette zutrifft, also 5555. Dabei besteht die Möglichkeit, dass Karl wegen seiner schlechten Noten eine über den Kopf gewischt bekommen hat und nun alles vierfach sieht. Auch wenn wir es so schreiben würden:

654321{4}

bezieht sich die geschweifte Klammer immer nur auf das vorherige Zeichen, also muss die Zahl so aussehen wenn sie von uns anerkannt werden will:

654321111

Die rechteckige Klammer ist also unser Zufallsgenerator. Zur Verdeutlichung ein kurzes Beispiel mit der Postleitzahl:

[0-9]{5}

würde verlangen, dass die eingegebene Postleitzahl nur aus Zahlen besteht und 5 Zeichen lang sein muss, also immer ein 5 stelliger Wert von dem Besucher verlangt wird. Natürlich können wir so nicht feststellen ob die Postleitzahl existiert (vorallem wenn der Benutzer nur Nullen eintippt), aber auch wenn wir die Syntax einer eMail oder die der des Straßennamens überprüfen, ja selbst wenn wir die Eingabe des Alters verlangen, können wir nicht wissen, ob diese Eingaben der Wahrheit entsprechen, außer wir können auf eine Liste zugreifen, die all diese Daten enthält und entsprechend vergleicht ob es etwa eine "Sinn des Lebens – Straße 42" in Berlin gibt.

Zurück zur Note: Ein regulärer Ausdruck liest sich von links nach rechts, so wie wir es vom lesen gewöhnt sind. [1-6]{1} steht daher am Anfang. Jetzt gilt es das Plus und das Minus hinzuzufügen.

[1-6]{1}[-+]{1}

Nanu, was ist das denn? Wir können ja nur eine 3+ oder 3- bekommen, aber keine 3 mehr. Und war der Bindestrich nicht ein Sonderzeichen? Ja, das war er. Aber da er ja nichts verbindet, weil er am Anfang steht, müssen wir ihn auch nicht als Sonderzeichen betrachten, da er nicht von einem Ausgangswert ausgeht um ihn mit einem anderen zu verbinden. Wollten wir jetzt zum Beispiel bei der Prüfung eines Domainnamens nur Zahlen und den Bindestrich zulassen, schreiben wir das einfach mit [-0-9] bzw. [0-9-] oder wir maskieren einfach das Sonderzeichen mit einem Backslash und können es auch so aufschreiben [0-4\-5-9]. Aber aufgepasst: Das + Zeichen ist bei regulären Ausdrücken auch ein Sonderzeichen. In einer Zeichenklasse wird es nicht umgewandelt, steht es aber vor oder nach den eckigen Klammern, maskieren wir es einfach, während der Bindestrich nur in Zeichenklassen gilt und nicht mit einem Backslash \ maskiert werden muss:

[1-6]{1}\+{1} oder [1-6]{1}-{1}

Was aber unser Problem noch nicht ganz löst. Doch mit den geschweiften Klammern kann man noch weitaus mehr machen, als nur einen genauen Zahlenwert anzugeben. Wir können auch festlegen, zwischen welcher Zeichenlänge das vorherige Zeichen auftreten darf. Das geht so:

{Mindestlänge, Maximallänge}

in unserem Beispiel also {0,1}, weil es nicht vorkommen muss und wenn es vorkommt, dann nur 1 mal. Eine andere Schreibweise für {0,1} ist das Sonderzeichen ? womit diese beiden Ausdrücke den gleichen Zweck erfüllen:

[-+]{0,1} ist äquivalent zu [-+]?

Andere verkürzte Schreibweisen erhält man mit dem Pluszeichen und dem Multiplikationszeichen, dem Stern *, hier eine kurze Liste dazu:

  •  ? entspricht {0,1}
  • + {1,}
  • * {0,}

+ und * sind sich also ähnlich. Bei Plus muss das vorherige Zeichen mindestens einmal vorkommen und danach beliebig. Daher wurde der zweite Wert offen gelassen. Bei dem Stern braucht das vorherige Zeichen nicht aufzutauchen und wenn es auftaucht, kann es das so oft es will. Nehmen wir zu Verdeutlichung Karl zur Hand.

Karl?   Bedeutet, dass Kar sowie auch Karl richtig sind. Was anderes wird nicht akzeptiert.
Karl+   Heißt, wir wollen Karl haben oder einen langen Karl, also Karlll und Karllllllll und Karlllllllllll und wie lang die Karls alle sein mögen.
Karl*   Ist wie mit dem Plus, nur dass Kar auch akzeptiert wird. * ist also die toleranteste Form.

Daneben gibt es auch noch die Möglichkeit {,Maximalwert} zu schreiben, falls alle Werte davor zugelassen werden sollen.

Tja, dann ist wohl unsere Notenform mit [1-6]{1}[-+]? fertig? Tja, ich muss ersteinmal sagen: Nein. Zwar ist der Besucher jetzt dazu angehalten, in das Forumlar eine Zahl (in Verbindung mit einem – oder +) zu schreiben, aber er kann immernoch 666 oder a6+ad6-6 schreiben, denn wir prüfen ja nur ob unser Ausdruck irgendwo in der Eingabe des Benutzers vorkommen, nicht aber womit die Eingabe anfangen oder aufhören muss. Aber hier ist das aber nurnoch ein Kinderspiel. Mit dem Zirklumflex ^ (oder Caret) weisen wir an, dass die Eingabe von Anfang an mit dem darauffolgenden Zeichen starten muss, also:

^[1-6]

bedeutet, die Eingabe muss mit einer Zahl von 1 bis 6 anfangen, sonst ist sie schon falsch. Mit dem Dollarzeichen $ regeln wir, dass die Eingabe mit dem vorhergegangenen Zeichen aufhören muss, also

^[1-6][-+]?$

heißt, wir haben unsere Note. Dahinter darf nicht mehr kommen, und davor auch nicht. Ließe man das Dollarzeichen in diesem Falle weg, müsste zwar die Eingabe mit einer realen Note anfangen, dahinter könnte aber auch stehen "Ich habe dich reingelegt". Genauso ist es umgekehrt wenn wir den Zirklumflex weglassen. Dann könnte der Besucher erst schreiben "So leicht kriegst du mich nicht" und mit der Note aufhören, da sie ja wie gewünscht am Ende der Zeichenkette stehen soll. ^$ erzwingt also, dass alles was da zwischen ist genau so von Anfang bis Ende in der Zeichenkette vorkommen muss.

Bisher ist alles gut gelaufen. Aber schon einmal eine 6- bekommen? Manche Lehrer wünschten es ginge noch darüber hinaus, aber bei der 6 ist leider Schluss. Andererseits bin ich damit auch zufrieden. Doch Kopf hoch, die Lösung ist sehr simpel mit dem Strich |, der soviel wie "oder" bedeutet wie wir es sonst mit den zwei Strichen || aus den Programmiersprachen kennen.

Also, wir betrachten das auch genauso wie diese Oderzeichen. Alles was links davon ist, ist ein Teil für sich und alles was rechts davon steht ist sein eigener Teil für sich. Es wird nicht mehr nur zwischen dem Zeichen links oder rechts geprüft, sondern alles was links oder rechts steht.

^[1-5]{1}[-+]?|6\+?$

Gehen wir zur Prüfung nochmal den Ausdruck durch. Anfangen muss er mit einer Zahl zwischen 1 und 5 gefolgt von einem – oder +, wenn wir eines haben ODER er beginnt mit einer 6 auf die höchstens nur ein + folgen kann, also kein -. Dabei darf nicht vergessen werden: + ist und bleibt ein Sonderzeichen, ohne den Backslash (6+) würde das bedeuten, die 6 einmal oder unendlich viele angereihte 6en (+) und das einmal oder keinmal (?). Eine leere Eingabe würde daher genauso richtig sein wie die Ziffer 666 oder 666666, denn einmal unendlich ergibt unendlich und keinmal unendlich ergibt nichts. Somit sind wir jetzt mit der Notenprüfung fertig und können eben das Array neben unseren regulären Ausdruck stellen:

var Noten = new array("1+","1","1-","2+","2","2-","3+","3","3-","4+","4","4-","5+","5","5-","6+","6");

und

^[1-5]{1}[-+]?|6\+?$

Noch eine Verkürzung gibt es dann bei der Prüfung der möglichen Werte mit der Eingabe, die wir im dritten Kapitel kennenlernen, wenn wir uns mit dem Einbau in Javascript beschäftigen. Jetzt ist aber schon erkennbar: Wollen wir einfach nur ein paar Werte streichen, zum Beispiel weil bei der Annahme der Daten ein Notendurchschnitt von 3 gefordert wird müssen wir nur 2 Zahlen ändern, während im Array die Hälfte der Werte gestrichen wird. Möchten wir aber höhere Werte zulassen, müssen wir beim Array mühevoll jeden Wert wieder eintragen. Wir merken also schon bei so einer simplen Aufgabe einen deutlichen Mehrwert gegenüber dem traditionellen Weg. Später werden wir uns noch mit der Schnelligkeit (Performance) beschäftigen.


Kurze Zusammenfassung: Alle Sonderzeichen, die als Teil einer Zeichenkette benutzt werden wollen, ohne dass sie ihre Funktion als Sonderzeichen ausführen, müssen mit einem Backslash \ versehen werden. Bisher sind das die eckigen Klammern, das Fragezeichen, das Pluszeichen, das Dollarzeichen, der Zirklumflex, der Stern, der Strich und nicht zu vergessen, der Backslash selbst. Der Bindestrich gilt nur in Zeichenklassen und muss ggf. nur dort maskiert werden, wenn er nicht am Anfang oder Ende steht. Im nächsten Kapitel werden noch ein paar wenige Sonderzeichen hinzukommen. Das sind dann die normalen Klammern, der Slash und der Punkt. Außerdem wird die Sache mit dem Strich noch kurz vertieft.