preg_match

(Auszug aus "Reguläre Ausdrücke" von Jeffrey E. F. Friedl)

preg_match(pattern, suchstring [, treffer [, flags [, offset ]]])

Parameter

pattern

Der Pattern-Parameter: eine Regex mit Begrenzungszeichen, mit optionalen Modifikatoren (siehe Der »Pattern«-Parameter).

suchstring

Der Text, der abgesucht werden soll.

treffer

Optionale Variable, die die Trefferdaten enthält.

flags

Optionale Flags, die das Verhalten des Matchings beeinflussen. Gegenwärtig ist nur ein Flag definiert, PREG_OFFSET_CAPTURE (siehe Trefferdetails herausholen: PREG_OFFSET_CAPTURE).

offset

Offset im Suchstring (nullbasiert), bei dem die Suche beginnen soll (siehe Der offset-Parameter). In der deutschen PHP-Dokumentation als Versatz bezeichnet.

Rückgabewert

Wahr, wenn ein Treffer gefunden wurde, sonst falsch.

Erläuterungen

In der einfachsten Form

preg_match($pattern, $suchstring)

gibt preg_match einen Wahrheitswert zurück, der besagt, ob zu der Regex in $pattern irgendwo in $suchstring ein Treffer gefunden wurde. Hier ein paar Beispiele:

if (preg_match('/\.(jpe?g|png|gif|bmp)$/i', $url)) {
  /* Offenbar eine URL von einem Bild */
}
if (preg_match('{^https?://}', $uri)) {
  /* Protokoll-Teil des URI ist http oder https */
}
if (preg_match('/\b MSIE \b/x', $_SERVER['HTTP_USER_AGENT'])) {
  /* Der verwendete Browser ist IE */
}

Trefferdaten auswerten

Das optionale dritte Argument von preg_match ist eine Variable, in der Werte der Trefferdaten zurückgegeben werden. Man kann natürlich jede Variable nehmen, ich verwende in den Beispielen »$treffer«. Aber auch wenn ich ohne spezifisches Beispiel »$treffer« verwende, bedeutet das in Wirklichkeit »die Variable, die als drittes Argument von preg_match verwendet wird«.

Nach einer erfolgreichen Mustersuche gibt preg_match den Wert wahr zurück und setzt $treffer wie folgt:

$treffer[0]
$treffer[1]
$treffer[2]
...
auf den gesamten Treffertext
auf den Text, auf den das erste Klammerpaar gepasst hat
auf den Text, auf den das zweite Klammerpaar gepasst hat

Wenn benannte Klammerausdrücke verwendet werden, werden die entsprechenden Werte ebenfalls zurückgegeben. Ein Beispiel dazu folgt im nächsten Abschnitt.

Hier ein Beispiel, das wir schon unter Regex-Methoden aus der Praxis kennengelernt haben (siehe unter Dateinamen aus einem Pfadnamen herauslösen):

/* Dateinamen aus einem vollen Pfadnamen extrahieren */
if (preg_match('{ / ([^/]+) $}x', $pfad, $treffer))
  $DateiName = $treffer[1];

Man darf die $treffer-Variable (oder wie immer sie genannt wird) nur dann auswerten, wenn preg_match den Wert wahr zurückgegeben hat. Falsch wird zurückgegeben, wenn kein Treffer gefunden wird oder wenn ein Fehler auftritt (Syntaxfehler im Muster, unzulässige Flags usw.). Bei manchen Fehlern wird $treffer gelöscht, d.h. man bekommt ein leeres Array zurück. Bei anderen bleibt $treffer unberührt und enthält die Werte aus der vorherigen, erfolgreichen Mustersuche. Man kann also aus einem nicht-leeren $treffer-Array nicht den Schluss ziehen, dass ein Treffer gefunden wurde.

Hier ein etwas komplizierteres Beispiel mit drei einfangenden Klammerpaaren:

/* Protokoll, Hostname und Port aus einer URL herausholen */
if (preg_match('{^(https?):// ([^/:]+) (?::(\d+))? }x', $url, $treffer))
{
  $proto = $treffer[1];
  $host  = $treffer[2];
  $port  = $treffer[3] ? $treffer[3] : ($proto == "http" ? 80 : 443);
  print "Protokoll: $proto\n";
  print "Hostname : $host\n";
  print "Port     : $port\n";
}

Leere Treffer-Elemente am Ende werden gekappt

Wenn ein Klammerpaar nicht Teil des gesamten Treffers ist, wird im entsprechenden Element des $treffer-Arrays der leere String (Anmerkung: Wenn Sie hier NULL statt dem leeren String erwarten, beachten Sie bitte den Kasten Ein leerer Treffer oder ein Nicht-Treffer.) zurückgegeben. Eine Warnung allerdings: Leere Elemente am Ende des Arrays werden in $treffer gar nicht erst zurückgegeben. Im obigen Beispiel kann es sein, dass ˹(\d+)˼ Teil des gesamten Treffers ist; $treffer[3] ist dann eine Zahl. Es kann aber auch sein, dass es nicht zum Treffer gehört, dann existiert das Element $treffer[3] gar nicht.

Benannte Unterausdrücke

Wir betrachten das gleiche Beispiel, verwenden hier aber benannte Unterausdrücke (siehe Benannte Unterausdrücke). Die Regex wird dadurch länger, aber auch selbsterklärend:

/* Protokoll, Hostname und Port aus einer URL herausholen */
if (preg_match('{^(?P<proto> https? ) ://
                  (?P<host>  [^/:]+ )
             (?: :(?P<port>  \d+    ) )?  }x', $url, $treffer))
{
  $proto = $treffer['proto'];
  $host  = $treffer['host'];
  $port  = $treffer['port'] ? $treffer['port'] : ($proto=="http"?80:443);
  print "Protokoll: $proto\n";
  print "Hostname : $host\n";
  print "Port     : $port\n";
}

Weil diese benannten Unterausdrücke aussagekräftiger sind, kann das Umkopieren von $treffer in einzelne Variablen auch unterbleiben. In solchen Fällen wird man aber einen anderen Namen als $treffer nehmen, etwa wie folgt:

/* Protokoll, Hostname und Port aus einer URL herausholen */
if (preg_match('{^(?P<proto> https? )://
                  (?P<host> [^/:]+  )
            (?: :(?P<port> \d+     )  )?  }x', $url, $UrlInfo))
{
  if (! $UrlInfo['port'])
     $UrlInfo['port'] = ($UrlInfo['proto'] == "http" ? 80 : 443);
  echo "Protokoll: ", $UrlInfo['proto'], "\n";
  echo "Hostname : ", $UrlInfo['host'], "\n";
  echo "Port     : ", $UrlInfo['port'], "\n";
}

Wenn man benannte Klammerausdrücke benutzt, werden die Elemente für die durchnummerierten Klammerpaare dennoch in $treffer zurückgegeben.

Wenn das obige Programmbeispiel auf die $url"http://regex.info/"‹ angewendet wird, enthält $UrlInfo:

array
(
    0       => 'http://regex.info',
    'proto' => 'http',
    1       => 'http',
    'host'  => 'regex.info',
    2       => 'regex.info'
)

Mit dieser Doppelspurigkeit wird Speicherplatz verschwendet, aber das ist der Preis, den man für den Komfort und die Klarheit von benannten Unterausdrücken bezahlt. Ich rate davon ab, benannte und nummerierte Unterausdrücke zu mischen – außer bei $treffer[0] für den gesamten Treffer.

Beachten Sie, dass die Elemente 3 und ›port‹ nicht ausgegeben wurden; sie nehmen nicht am Treffer teil und wurden als letzte Elemente des $treffer-Arrays gekappt (siehe Leere Treffer-Elemente am Ende werden gekappt).

Übrigens: Obwohl man für die Namen von benannten Klammern auch Zahlen angeben kann, z.B. ˹(?P<2>...)˼, ist davon strikt abzuraten. Zudem wird diese paradoxe Situation in PHP 4 anders behandelt als in PHP 5, und in keinem Fall so, wie man es erwarten würde. Es ist wirklich am besten, auf numerische Namen für Klammern zu verzichten.

Trefferdetails herausholen: PREG_OFFSET_CAPTURE

Wenn man als viertes Argument bei preg_match den Wert PREG_OFFSET_CAPTURE (den einzigen momentan erlaubten) angibt, sind die Elemente des $treffer-Arrays nicht mehr bloße Strings, sondern Unterarrays mit jeweils zwei Elementen. Das erste dieser Elemente ist der von der entsprechenden Klammer eingefangene Text, das zweite ist der Offset vom Anfang des Suchstrings bis zum eingefangenen Text (oder -1, wenn der entsprechende Klammerausdruck nicht Teil des Treffers war).

Diese Offsets sind nullbasiert und beziehen sich immer auf den Anfang des Suchtexts, auch dann, wenn mit dem fünften $offset-Argument ein Start-Offset angegeben wurde. Der Offset wird auch dann in Bytes zurückgegeben, wenn der u-Modifikator verwendet wird (siehe unter PHP-spezifische Modifikatoren).

Als Beispiel betrachten wir das HREF-Attribut in einem Link-Tag. Ein Attributwert in HTML kann in doppelten Anführungszeichen, in Hochkommas oder ganz ohne Anführungszeichen angegeben werden. Diese Werte werden in der folgenden Regex mit dem ersten, zweiten oder dritten Paar von einfangenden Klammern aus dem Tag herausgepflückt:

preg_match('/href \s*=\s* (?: "([^"]*)" | \'([^\']*)\' | ([^\s\'">]+) )/ix',
           $tag,
           $treffer,
           PREG_OFFSET_CAPTURE);

Wenn $tag den Wert

<a name=bloglink href='http://regex.info/blog/' rel="nofollow">

besitzt, wird ein Treffer gefunden, und die Variable $treffer enthält:

array
(
    /* Daten für den gesamten Treffer */
    0 => array ( 0 => "href='http://regex.info/blog/'",
                 1 => 17 ),
    /* Daten für das erste einfangende Klammerpaar */
    1 => array ( 0 => "",
                 1 => -1 ),
    /* Daten für das zweite einfangende Klammerpaar */
    2 => array ( 0 => "http://regex.info/blog/",
                 1 => 23 )
)

$treffer[0][0] ist der Text des gesamten Treffers, und $treffer[0][1] besagt, an welcher Position in $tag dieser Treffer beginnt.

Den String in $treffer[0][0] hätten wir auch so erhalten können:

substr($tag, $treffer[0][1], strlen($treffer[0][0]));

$treffer[1][1] ist -1, das besagt, dass das erste Klammerpaar keinen Anteil am gesamten Treffer hat. Das dritte auch nicht, aber leere Elemente am Ende des $treffer-Arrays werden gar nicht zurückgeliefert (siehe oben Leere Treffer-Elemente am Ende werden gekappt).

Der offset-Parameter

Wenn bei preg_match das Argument offset angegeben wird, beginnt die Regex-Maschine mit der Treffersuche erst beim Byte mit diesem Offset im Suchstring. Wenn der Offset negativ ist, wird er vom Ende des Suchstrings her gemessen. Normalerweise wird ab Offset 0 (also vom Anfang des Suchstrings) gesucht.

Der Offset muss in Bytes angegeben werden, auch dann, wenn der u-Modifikator verwendet wird. Wenn man einen ungültigen Offset angibt (einen, der mitten in eine Multibyte-Sequenz fällt), dann schlägt die Mustersuche ohne Warnung fehl.

Wenn die Suche bei einem Offset ungleich 0 beginnt, bezieht sich das Metazeichen ˹^˼ dennoch auf den Anfang des gesamten Suchstrings. Der Offset bezeichnet ganz einfach die Stelle im Suchstring, an der der Trefferversuch beginnt. Ein Lookbehind-Konstrukt kann beispielsweise den Text vor dem Offset »sehen«.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema Reguläre Ausdrücke bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:
   

Copyright der deutschen Ausgabe © 2008 by O’Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Reguläre Ausdrücke" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O’Reilly Verlag GmbH & Co. KG, Balthasarstr. 81, 50670 Köln