quakenet:#php Tutorial

Author: Progman, zuletzt bearbeitet von progman @ 2004/06/10 08:59:45

Bitte beachten Sie, dass die Tutorialkapitel zusammenhängen. Wenn sie direkt auf ein Kapitel verlinkt wurden müssen Sie gegebenenfalls die vorherigen Kapitel auch lesen. Achten Sie beim lesen darauf, dass Sie kein Kapitel überspringen.

Reguläre Ausdrücke

  1. Was sind Reguläre Ausdrücke
  2. Aufbau von PCRE
  3. Wofür braucht man Regex?
  4. Regex in PHP benutzen
  5. Suche nach einem Teilstring
  6. Alternative Auswahl
  7. Zusammenfassen und Teileigenschaften bilden
  8. Zugriff auf Ergebnisse von subpattern
  9. Sammelgruppen
  10. Wiederholungen von Regulären Ausdrücken
  11. Regex haben hunger
  12. String Anfang und Ende
  13. Die Kunst des Regex

1. Was sind Reguläre Ausdrücke

Wenn wir uns die 2 Wörter Reguläre Ausdrücke angucken können wir uns erstmal nix darunter vorstellen. Nachdem wir im Duden das Wort regulär nachgeschlagen haben stellen wir fest, dass regulär für der Regel entsprechend steht. Ein Regulärer Ausdruck ist also ein Regel entsprechender Ausdruck. In PHP muss man es aber von der anderen Seite sehen, ein Ausdruck der eine Regel definiert.

Es gibt 2 Regex-Engines (Regex ist die Kurzform von "Regulärer Ausdruck"). Einmal ist es die POSIX-Engine und die Perl-Kompatible-Regex-Engine, kurz PCRE, die ca. 200mal schneller ist als die POSIX-Engine. Deswegen werden wir auch nur die PCRE-Engine benutzen. POSIX ist aber so ähnlich aufgebaut wie PCRE.

2. Aufbau von PCRE

Ein Perl-Regex ist wie folgt aufgebaut.

Delimiter + Regex + Delimiter [+ Modifiers]
        

Der Delimiter gibt das Trennzeichen an von der der Regex von den Modifiers getrennt werden. Dieses Trennzeichen muss ein nicht Alphanumerisches-Zeichen sein, darf also kein Buchstabe und keine Zahl sein. Man benutzt z.B. die Zeichen # und die in Perl üblichen Zeichen /. Man sollte ein Delimiter wählen, den man nicht im Regex benutzt, da man sonst dieses Zeichen im eigentlichem Regex escapen muss.

Nach dem Delimiter kommt dann der eigentliche Regex. Dieser Regex bestimmt dann wie der übergeben Ausdruck aussehen soll (üblichweise ein String). Dies kann ein sehr komplexer Ausdruck sein, kann aber auch nur ein Zeichen sein.

Nach dem Regex kommt wieder der vorher gewählte Delimiter. Das symbolisiert das Ende des gesamten Regex.

Nach dem 2. Delimiter kommen die Modifier. Mit diesen Modifier kann man noch den Regex ein wenig einstellen. Modifier werden dabei in einfache Buchstaben angegeben. Jeder Buchstabe steht für eine Eigenschaft für den Regex. Diese Angabe ist Optional.

3. Wofür braucht man Regex?

Hier sind ein paar Anwendungsbeispiele wo man Regex gebrauchen könnte.

  1. Regex kann man zum Beispiel benutzen um ein Eingabefeld auf Gültigkeit zu prüfen. Dies macht man z.B. bei einer Eingabe einer Emailadresse oder Spielernamens.

  2. Manchmal möchte man aus einem fertigen String, z.B. von einer anderen Seite, bestimmte Informationen auslesen. Diese kann man dann in seinem Script weiter verarbeiten. Wenn möglich sollte man aber sscanf benutzen. Man sollte nicht immer die Regex-Engine anwerfen.

  3. Mit Regex kann man auch Teilstrings in einem String ersetzen lassen die man nicht ohne weiteres mit str_replace ersetzen lassen kann. Für Smilies braucht man z.B. kein Regex, die kann man einfach mit str_replace in die Bilder umwandeln.

4. Regex in PHP benutzen

Um auf einen String ein Regex anzuwenden benutzt man die Funktion preg_match. Als 1. Parameter gibt man den Regex an und als 2. Parameter den String den man untersuchen möchte. Den Regex sollte man in einfachen Anführungszeichen schreiben, damit man nicht wild escapen muss. Ein Beispiel könnte so aussehen.

<?php
    error_reporting
(E_ALL);
    
    if(
preg_match('/foobar/', $_POST['text'])) {
        echo
"Regex matched";
    } else {
        echo
"Regex hat nicht gepasst";
    }
?>

Unter Linux kann man das Programm pcretest benutzen um die hier besprochenden Regexe zu testen. Wenn man keine Linuxkiste hat, kann man auch folgendes PHP-Script verwenden.

<?php
    error_reporting
(E_ALL);
    
$regex = 'hier der regex';

    if(isset(
$_POST['eingabe'])) {
        echo
"Eingabe:<br />\n";
        echo
"<pre>\n";
        echo
$_POST['eingabe'];
        echo
"</pre>\n";
        if(
preg_match($regex, $_POST['eingabe'])) {
            echo
"<span style=\"color: #008000\">passt</span><br />\n";
        } else {
            echo
"<span style=\"color: #FF0000\">passt nicht</span><br />\n";
        }
    }
    echo
"Regex:<br />\n";
    echo
"<pre>\n";
    echo
$regex;
    echo
"</pre>\n";
    echo
"<form action=\"regex.php\" method=\"POST\">\n";
    echo
"    <input type=\"text\" name=\"eingabe\" />\n";
    echo
"    <input type=\"submit\" name=\"testen\" />\n";
    echo
"</form>\n";

?>

5. Suche nach einem Teilstring

Wir fangen jetzt erstmal ganz klein an. Wir suchen erstmal einen Sring im String. Wir haben folgenden Regex.

/foobar/
        

Hier sieht man das der Delimiter / benutzt wurde. Dieser Regex passt auf jeden Text wo der String foobar vorhanden ist. Dieses 'passen' nennt man auch matchen, ein eingedeutsches Wort von match, daher auch der Funktionsname preg_match. Hier einige Beispiele, als Testprogramm wurde pcretest verwendet.

  re> /foobar/
data> ein String
No match
data> foobar
 0: foobar
data> ein String mit foobar
 0: foobar
data> rfunrufneifufoobaroevrneurvn
 0: foobar
data> foobaR
No match
data> FOOBAR
No match

        

Dieses re> ist Teil von pcretest. Dort gibt man dann den Regex ein, hier /foobar/. Bei data> kann man dann eingeben, was man prüfen möchte. Was diese 0 erstmal bedeutet lassen wir außen vor. Hier sieht man das im 1. String der Teilstring nicht gefunden wurde. In den letzen beiden Teststrings steht zwar auch foobar, doch der Regex matched nicht. Das liegt daran das Regex ohne Einstellungen Case-Sensitiv sind, dass heißt das Groß- und Kleinschreibung beachtet wird. Damit der Regex darüber hinwegsieht muss man den Modifier i benutzen. Das i kommt dabei von case-insensitiv.

  re> /foobar/i
data> foobar
 0: foobar
data> FOOBAR
 0: FOOBAR
data> FoObAr
 0: FoObAr
        

Dieses i gilt dabei für den kompletten Regex. Wenn man nur einen bestimmten Teil case-insensitiv matchen möchte so muss man diesen Teil in Klammern setzen, falls schon nicht vorhanden, und ein (?i) nach der Klammer auf schreiben. Aber zu dieser Schreibweise kommen wir später.

Wenn man nur nach einem Teilstring suchen möchte muss man nicht ein Regex benutzen. Es reicht auch wenn man Funktionen wie strstr, stristr oder substr_count benutzt. Diese sind um einiges schneller als ein Regex, da diese nicht die Regex-Engine anwerfen müssen, nur um zu gucken ob ein String in einem anderen String ist.

6. Alternative Auswahl

Manchmal wollen wir ein String matchen der nur aus bestimmten Teilstrings bestehen darf. Eine Alternative Auswahl erstellt man mit dem Zeichen |.

/foo|bar/
        

Dieser Regex passt z.B. nur auf Strings, die foo oder bar enthalten. Die Auswahl kann natürlich erweitert werden.

/foo|bar|bla|blo/
        

Dieser Regex matched z.B. auf einen String, wo ein Teilstring einer dieser 4 Möglichkeiten sein muss.

  re> /foo|bar|bla|blo/
data> roivnwev
No match
data> foobar
 0: foo
data> blablo
 0: bla
data> blobla
 0: blo
data> rfed
No match
data> ble
No match
data> fobablarfgblalroblaoloablrooa
 0: bla
        

Dies ist so als würde man folgenden PHP Code benutzen.

<?php
  
// ...
  
if(substr_count($var, "foo") OR
     
substr_count($var, "bar") OR
     
substr_count($var, "bla") OR
     
substr_count($var, "blo")) {
  
// ...
?>

7. Zusammenfassen und Teileigenschaften bilden

Bei einem etwas komplexeren Regex kann es vorkommen, dass man einen Teilregex eine bestimmte Eigenschaft geben muss die die anderen Teilregexe nicht haben sollen. Ein Modifier könnte man nicht mehr benutzen, denn der wirkt sich ja auf den ganzen Regex aus.

Um einen Teilregex anderen Eigenschaften zu vergeben muss man diesen erstmal in runden Klammern setzen. Am Anfang nach der Klammer kann dann folgender Ausdruck kommen.

(?...)
        

Da wo jetzt die Punkte stehen können dann bestimme Zeichen wie Modifier stehen, aber auch andere Zeichen, zu denen wir später kommen.

Ein Beispielregex könnte so aussehen.

/((?i)foo)bar/
        

Dieser Regex matched auf folgenden Ausdruck: Ein String foo, der Case-insensitiv ist, und einem danach kommenden String bar welcher normal, also Case-sensitiv geschrieben werden muss. Hier sind einige Beispiel Teststrings.

  re> /((?i)foo)bar/
data> foobar
 0: foobar
 1: foo
data> FOObar
 0: FOObar
 1: FOO
data> fooBAR
No match
data> FoObar
 0: FoObar
 1: FoO
data> blaBLAFOObarbla
 0: FOObar
 1: FOO
        

Hier sieht man erstmal, dass in der Variante, wo bar großgeschrieben wurde, der gesammte Regex nicht gematched hat. In allen anderen Versuchen passte der Regex. Desweiteren ist diese Ausgabe noch etwas anders. Es gibt nun eine neue Zeile, die mit der Zahl 1 startet. Diese Zahlen, die 0 und die 1, sind Rückreferenzen zu den gefundenen Teilstrings. Teilregexe die in Klammern stehen nennt man auch subpattern.

8. Zugriff auf Ergebnisse von subpattern

Wenn wir ein Teilregex in Klammern schreiben, so können wir in PHP, und auch noch im Regex selber, auf den Wert welcher dort gematched wurde, zugreifen. Mit diesen Linux-Testprogramm pcretest können wir uns die Werte ausgeben lassen, die der Teilregex dort gematched hat. Je nach dem wieviele Klammern und Teilregexe wir benutzt haben steigt auch die Zahl, die bei 1 startet. Die Referenz zur Zahl 0 enthält den Wert des ganzen Regex. Dieser ist auch immer vorhanden, und kann auch in PHP ausgelesen werden. Zu Testzwecken sollten wir deshalb unser PHP-Script (für die Leute die kein pcretest haben) etwas umschreiben.

Der Funktion preg_match kann man einem 3. Parameter übergeben, welche eine Variable sein muss. Nach dem matchen stehen dann die Ergebnisse des Regex in Form von einem Array zu Verfügung. Arrayindex 0 ist dabei der gesammte gematchte Ausdruck und die anderen Arrayindize sind die entsprechende Teilausdrücke. Das Script passen wir wie folgt an:

<?php
    error_reporting
(E_ALL);
    
$regex = 'hier der regex';

    if(isset(
$_POST['eingabe'])) {
        echo
"Eingabe:<br />\n";
        echo
"<pre>\n";
        echo
$_POST['eingabe'];
        echo
"</pre>\n";
        if(
preg_match($regex, $_POST['eingabe'], $matches)) {
            echo
"<span style=\"color: #008000\">passt</span><br />\n";
            echo
"<pre>\n";
            foreach(
$matches as $key => $value) {
                echo
$key." :".$value."\n";
                
// Kein <br />, da wir im <pre>-Element schreiben
            
}
            echo
"</pre>\n";
        } else {
            echo
"<span style=\"color: #FF0000\">passt nicht</span><br />\n";
        }
    }
    echo
"Regex:<br />\n";
    echo
"<pre>\n";
    echo
$regex;
    echo
"</pre>\n";
    echo
"<form action=\"regex.php\" method=\"POST\">\n";
    echo
"    <input type=\"text\" name=\"eingabe\" />\n";
    echo
"    <input type=\"submit\" name=\"testen\" />\n";
    echo
"</form>\n";

?>

Wenn wir nicht wollen, dass ein Teilregex durchnummeriert wird, wir aber auf die Klammern nicht verzichten können, müssen wir nach der Klammer auf ein ?: schreiben. Dann kann wie üblich eine Teileigenschaft, z.B. (?i), kommen und dann muss wie üblich der Teilregex folgen. Ein Beispiel könnte so aussehen.

  re> /(?:foo)bar/
data> foobar
 0: foobar

  re> /(foo)bar/
data> foobar
 0: foobar
 1: foo
        

Im Regex kann man auf so einen Wert noch im ganzen Regex zugreifen. Auf ein Ergebnis von einem vorherstehenden Teilregex greift man mit \x zu, wobei das x die Nummer des Teilregex ist. 0 kann man nicht benutzen. Wie denn auch, denn der Wert ist bei der Abarbeitung noch nicht gesetzt. So eine Referenz auf ein Teilregex braucht man z.B. bei preg_replace. Hier erstmal ein normales Beispiel für eine Teilreferenz.

  re> /((?i)foo)bar\1/
data> foobar
No match

data> foobarfoo
 0: foobarfoo
 1: foo

data> fOObarfOO
 0: fOObarfOO
 1: fOO
        

Dieser, in der Tat schwachsinnige, Regex passt nur auf ein String, wo ein Teilstring mit foo startet (case-insensitiv) darauf dann bar folgt und dann wieder der selbe erste Ausdruck folgen muss, der in der Klammer gematched wurde. In PHP ist \ normalerweise das Escape-Zeichen. Da wir aber den Regex immer in ' schreiben, wird dieses Zeichen nicht als Escape-Zeichen verwendet (außer bei \\) sondern wird in den String als solches gespeichert. Somit bekommt die Regex-Engine dieses Zeichen und kann entsprechend darauf reagieren.

9. Sammelgruppen

Wir haben nur nach festen Stringketten gesucht. Wenn wir aber z.B. nach einer Zahl suchen wollen müssten wir folgenden Regex benutzen.

/0|1|2|3|4|5|6|7|8|9/
        

Oder wenn wir nach Vokalen suchen möchten, müssten wir diesen Regex benutzen.

/(?i)a|e|i|o|u/
        

Es ist aber möglich, Zeichenklassen zu erstellen. Diese erstellt man mit den eckicken Klammern. Innerhalb der eckicken Klammern kommen die Zeichen rein, die diese Zeichenklasse matchen soll.

/[aeiou]/
        

Dieser regex matched auf ein kleines Vokal in einem String.

  re> /[aeiou]/
data> foobar
 0: o
data> bla
 0: a
data> BLA
No match
        

Wenn man ein Zeichen matchen möchte, welches nicht in dieser Zeichenklasse vorhanden ist, so muss man nach der eckicken Klammer-Auf ein ^ schreiben.

  re> /[^aeiou]/
data> foobar
 0: f
data> bla
 0: b
data> BLA
 0: B
data> a
No match
        

Wenn man ^ in einer Zeichenklasse benutzen möchte, muss man entweder dieses Zeichen escapen oder es nicht an erster Stelle schreiben. Das gleiche gilt auch für die Zeichen [ und ], da diese sonst zu früh die Zeichenklasse beenden würden.

In den Zeichenklassen kann man auch sowas wie von-bis benutzen. Dies könnte dann so aussehen:

  re> /[1-3][5-6]/
data> 15
 0: 15
data> 20
No match
data> 36
 0: 36
data> 24
No match 

  re> /[a-e][F-J]/
data> aF
 0: aF
data> dH
 0: dH
data> er
No match
        

Beachten sie das solche von-bis Konstruktionen Zeichenweise geht. Ein Ausdruck wie [0-24], um ein Uhrzeit zu erfassen, wird nicht gehen. Diese Zeichenklasse würde die Zahlen 0,1,2 oder 4 erfassen, aber nicht solche Zahlen von 3 und 5 bis 24.

PCRE kennt eine Menge von vordefinierten Zeichenklassen. Diese müssen nicht unbedingt extra in eine Zeichenklasse mit [] geschrieben werden, es sei denn man möchte solche vordefinierten Zeichenklassen zusammenpacken.

Hier nochmal in Kürze was welche Zeichenklasse matched.

\d = [0-9]
\D = [^0-9]
   = [^\d]
\w = [0-9a-zA-Z_]
   = [\da-zA-Z_]
\W = [^0-9a-zA-Z_]
   = [^\da-zA-Z_]
   = [^\w]
\s = [ \n\t\r\v\f]
\S = [^ \n\t\r\v\f]
   = [^\s]
.  = [^\n] (ohne Modifier s)
.  = [\x00-\xFF] ,also alle Zeichen (mit Modifier s)
        

10. Wiederholungen von Regulären Ausdrücken

Bestimmte Teile eines Regex kann man wiederholen lassen. Dies macht man um z.B. eine Zahl aus 10 Ziffern zu matchen. Normalerweise würden wir das so machen.

/\d\d\d\d\d\d\d\d\d\d/
        

Dies kann man natürlich zusammenfassen. Um ein Regex-Teil um eine bestimmte Anzahl wiederholen zu lassen, muss man diesen ggf. erstmal in Klammern schreiben. Danach kommt dann ein Ausdruck in geschweiften Klammern. Innerhalb dieser geschweiften Klammern kommt dann die Zahl hin, wie oft dieser Teil-Regex wiederholt werden soll, damit der Regex passt. In diesem Fall eine 10.

  re> /\d{10}/
data> 48957
No match
data> 4373648735683467
 0: 4373648735
data> foo134frrf324234bla1234567890jonv
 0: 1234567890
        

Versucht nicht eine 0 dort reinzuschreiben, also {0}. Dies wird nicht so gehen wie man es vielleicht möchte. Dazu gibt es andere Techniken.

Statt einer festen Anzahl von Wiederholungen kann man auch einen Teilregex variable wiederholen lassen. Dazu gibt man in den geschweiften Klammern 2 Zahlen mit Komma getrennt an. Die linke Zahl steht für das Minimum der Wiederholungen und die rechte Zahl für das Maximum der Wiederholungen. Ein Beispiel könnte so aussehen.

  re> /\d{3,7}/
data> foo324berv
 0: 324
data> 23
No match
data> 1234535
 0: 1234535
data> 12345678
 0: 1234567
        

Wenn man die Maximumangabe weglässt, also sowas wie {4,} schreibt, dann gibt es keine obere Grenze.

  re> /f{5,}/
data> ffff
No match
data> fffff
 0: fffff
data> 4ffff4ffff4
No match
data> ffffffffffffffffffffffffffffffff
 0: ffffffffffffffffffffffffffffffff
        

Es gibt auch bestimmte Zeichen, die für bestimmte Minimum/Maximum-Grenzen stehen. Diese Zeichen wird auch in anderen Bereichen im Internet finden, wie bei den DTDs und RFC-Seiten.

? = {0,1}
* = {0,}
+ = {1,}
        

Hier ein Beispiel.

  re> /\d+[a-z]*/
data> 45245
 0: 45245
data> 324uiu345bniu325
 0: 324uiu
data> uon
No match
        

Dieser Regex matched mindesdens eine Zahl. Danach können dann Buchstaben kommen

11. Regex haben hunger

Nehmen wir an wir wollen in einem Text alle URLs auslesen, und ggf. dann auch noch ersetzen, die in [url] [/url] stehen. Sowas kennt man von diversen Foren. Der dort stehende Text soll dann in einen HTML-Link umgewandelt werden. Um den Text aus diesen beiden Elementen zu erfassen benutzen man normalerweise folgenden Regex.

=\[url\](.*)\[/url\]=
        

Hier wurde das Gleichheitszeichen als Delimiter gewählt. Das habe ich deshalb gemacht, weil ich / selber als Regexteil verwende. Damit der Regex die Zeichen [ und ] nicht als Zeichenklassen erkennen müssen diese Zeichen escaped werden. Die Funktion preg_quote dient dazu, einen übergeben String entsprechend zu escapen, damit PCRE sie nicht als PCRE-Steuerzeichen erkennt.

Wir nehmen nun mal ein Beispiel Text zum matchen.

  re> =\[url\](.*)\[/url\]=
data> Weitere Infos auf [url]http://tut.php-q.net/[/url].
 0: [url]http://tut.php-q.net/[/url]
 1: http://tut.php-q.net/
        

Da wir hier das .* in Klammern geschrieben haben, können wir dann später in PHP und im Regex darauf zugreifen (Index 1). Wir werden dann aber Probleme bei einem längeren Text mit 2 URLs haben.

 re> =\[url\](.*)\[/url\]=
data> Infos zu PHP auf [url]http://tut.php-q.net/[/url] und natürlich im
      PHP Manual [url]http://www.php.net/[/url].
 0: [url]http://tut.php-q.net/[/url] und natürlich im
    PHP Manual [url]http://www.php.net/[/url]
 1: http://tut.php-q.net/[/url] und natürlich im
    PHP Manual [url]http://www.php.net/
        

Da wo die 1 steht sieht man, dass nicht nur die URL gematched wurde, sondern auch noch viel mehr Text. Dabei hat der Regex das letze [/url] als Ende erkannt und matched somit alle anderen [url]-Element mit, obwohl uns das später stören würde. Denn dann würde ein Link wie <a href="http://tut.php-q.net/[/url] und natürlich im PHP Manual [url]http://www.php.net/">http://tut.php-q.net/[/url] und natürlich im PHP Manual [url]http://www.php.net/</a> erzeugt werden, der natürlich nicht funktionieren kann. Der Teil .* matched nicht so viel wie er braucht, sondern so viel wie er kann. Diesen Effekt nennt man gierig bzw. gefräßig und heißt greedy. Regexe wie * oder + versuchen so viel zu erfassen wie nur möglich. Dies kann in manchen Situationen, wie diese hier, unerwünscht sein. Es gibt diverse Techniken um diesen Effekt gegenzutreten.

  1. Wir benutzen den Modifier U, von Ungreedy. Damit matched ein Teilregex wie .* nur so viel, wie er braucht, damit der ganze Regex gültig ist. Wenn wir diesen Modifier setzen gilt er für den ganzen Regex. Hier ein Beispiel:

      re> /.+/
    data> matched alles
     0: matched alles
      re> /.+/U
    data> matched so wenig wie möglich
     0: m
                    

    Im ersten Regex, ohne U, matched er soviel er kann. Er versucht, das maximalste rauszuholen. Im zweiten Regex hingegen erfasst er so wenig wie möglich, bei einem .+ ist das ein Zeichen, hier der erste Buchstabe m.

    Der Regex mit der URL könnte nun so aussehen:

      re> =\[url\](.*)\[/url\]=U
    data> Infos zu PHP auf [url]http://tut.php-q.net/[/url] und natürlich im
          PHP Manual [url]http://www.php.net/[/url].
     0: [url]http://tut.php-q.net/[/url]
     1: http://tut.php-q.net/
                    
  2. Man kann auch das Suchmuster des Regex etwas einschränken. Das Metazeichen Punkt akzeptiert ja beliebige Zeichen (von \n abgesehen). Wir könnten also sagen, dass er nur nicht-Whitespaces benutzen soll (\S*), da eine URL aus keinem Leerzeichen oder so besteht. Wir können aber auch sagen, dass er alle Zeichen außer [ nehmen soll ([^\[]+), denn dann würde er vor dem schliessenden [/url] aufhören. Oder man kombiniert beides, damit auch kein Leerzeichen und so in der URL stehen darf ([^\s\[]*). Beispiel:

      re> /.*/
    data> ein text mit Leerzeichen
     0: ein text mit Leerzeichen
      re> /\S+/
    data> ein text mit Leerzeichen
     0: ein
    data>
                    

    Wir können aber auch ganz anders daran gehen und nicht sagen, was er nicht matchen soll, sondern sagen ihm, was er matchen darf, um etwas mehr Kontrolle zu haben. Der fragliche Regex-Teil könnte dann so aussehen.

    ([\w$-.+!*'()@:?=&/;]*)
                    

    Das darf alles in eine URL stehen. Nachlesbar in RFC 1738. Damit sagen wir welche Zeichen er nehmen darf. Die Zeichen " und § z.B. darf er nicht nehmen und die Whitespaces wie Leerzeichen und Tabulator schon garnicht. Beispiel:

      re> /.*/
    data> 123abc456
     0: 123abc456
      re> /[a-z3]+/
    data> 123abc456
     0: 3abc
      re> §\[url\]([\w$-.+!*'()@:?=&/;]*)\[/url\]§
    data> foo[url]http://foobar/[/url]bar
     0: [url]http://foobar/[/url]
     1: http://foobar/
    data> [url]url[/url]bla[url]foobar[/url]
     0: [url]url[/url]
     1: url
    
                    

    Das § ist zwar ein ungewöhnlicher Delimiter, doch ich konnte die üblichen nicht nehmen, ohne die Zeichen in der Zeichenklasse zu escapen.

  3. Eine andere Möglichkeit, einen Teilregex Ungreedy zu machen ist es, hinter dem Ausdruck ein ? zu schreiben, also sowas wie (.*?). Damit wird die aktuelle Einstellung von Greedy/Ungreedy umgestellt. Wenn der gesammte Regex ungreedy ist, so wird dieser Regexteil wieder greedy und umgekehrt. Beispiel:

      re> /.+/
    data> alles rein was geht
     0: alles rein was geht
      re> /.+?/
    data> alles rein was geht
     0: a
                    

    In diesem Fall:

      re> =\[url\](.*?)\[/url\]=
    data> [url]http[/url]andere[url]url[/url]
     0: [url]http[/url]
     1: http
                    

12. String Anfang und Ende

Regex können wir auch benutzen um Benutzereingaben zu validieren, also um zu gucken ob sie einen sinnvollen und gültigen Inhalt haben. Da haben wir aber im moment ein Problem. Die Regexe, die wir momentan schreiben, passen nur auf ein Teiltext, nicht auf den gesammten Text. Dies zeigt folgender Regex.

  re> /[a-z]+/
data> 7823467823grfre345245
 0: grfre
        

Wir wollten eigentlich ein Regex haben, der prüft, dass die Eingabe nur Buchstaben enthält. Doch dieser Regex findet zwar den Text, aber das davor und danach ist ihm scheiss egal. Im Regex gibt es aber die Möglichkeit dass der Regex von Anfang an und/oder bis zum ende gelten muss. Das Zeichen für den Anfang der Eingabe ist das Hochzeichen ^.

  re> /^foo/
data> blafoo
No match
data> foo
 0: foo
data> bla\nfoo
No match
        

Wenn man den Modifier m (von multiline) benutzt matched das Hochzeichen auf die Stelle nach einem Zeilenumbruch. Um genauer zu sein zwischen den Zeilenumbruch und den folgenden Zeichen.

  re> /^foo/m
data> blafoo
No match
data> foo
 0: foo
data> bla\nfoo
 0: foo
        

Um ein Ende zu erfassen benutzt man das Zeichen $.

  re> /bar$/
data> foo
No match
data> foobar
 0: bar
data> bar\nfoo
No match
        

Mit den Modifier m kann dieses Zeichen auch ein Zeilenumbruch erkennen, und nicht nur ein Stringende.

  re> /bar$/m
data> foo
No match
data> foobar
 0: bar
data> bar\nfoo
 0: bar
data>
        

Mit diesen Wissen können wir nun eine richtige Eingabe-Überprüfung schreiben. Wir benutzen beide Zeichen um den Regex mehr einzuschränken.

  re> /^[a-z]+$/
data> foobar
 0: foobar
data> 123
No match
data> FOOBAR
No match
        

Hier sollte man vielleicht noch den Modifier i verwenden.

13. Die Kunst des Regex

Dies ist nur ein Tutorialeintrag zu der Programmiersprache PHP. Es ist kein Ersatz für das lernen von Regex. Im PHP Manual liegt eine Mirror der PCRE-Manpage. Weitere Infos zu PCRE findet man unter http://www.php.net/pcre.

Fragen zum aktuellen Thema

  1. Was bedeutet gierig im Zusammenhang mit Regex?
  2. Was macht der Modifier s?
  3. Was matched /(bb|[^b]{2})/ ?
Was bedeutet gierig im Zusammenhang mit Regex?

Der Ausdruck, der mit * oder + auf eine unbestimmte länge erweitert wird, frist soviele Zeichen wie er nur kann. Dies kann in manchen Situationen unerwünscht sein. Um diese Effekt zu unterbinden kann man den Modifier U benutzen.

Was macht der Modifier s?

Dieser Modifier matched bei dem Punkt auch noch einen Zeilenumbruch \n. Dies braucht man um einen mehrzeiligen Text mit ((?U).*) zu erfassen.

Was matched /(bb|[^b]{2})/ ?

Dies ist keine ernsthafte Frage. Dieser Regex ist ein Scherz bezogen auf Shakespeare. Er ist die Regex schreibweise von "To be or not to be".

bb      To be
|       or
[^      not
{2}     to
b]      be
        

Nach oben