preg_match_all

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

preg_match_all(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

Variable, die die Trefferdaten enthält (nicht optional).

flags

Optionale Flags, die das Verhalten des Matchings beeinflussen. Mögliche Werte:

PREG_OFFSET_CAPTURE (siehe Das PREG_OFFSET_CAPTURE-Flag bei preg_match_all)

und/oder eines von den folgenden Flags:

PREG_PATTERN_ORDER (siehe Die Reihenfolge PREG_PATTERN_ORDER (Voreinstellung))
PREG_SET_ORDER (siehe Die Reihenfolge PREG_SET_ORDER)

offset

Offset im Suchstring (nullbasiert), bei dem die Suche beginnen soll (wie bei preg_match, siehe Der offset-Parameter).

Rückgabewert

preg_match_all gibt die Anzahl der erfolgreichen Mustersuchen zurück.

Erläuterungen

preg_match_all funktioniert im Prinzip wie preg_match, nur wird die Regex-Maschine nach einem erfolgreichen Matching immer wieder neu angesetzt, bis eine Mustersuche fehlschlägt. Jeder erfolgreiche Treffer generiert ein Array von Match-Daten, am Ende enthält die treffer-Variable deshalb ein Array von Arrays, wobei die inneren Arrays die Trefferdaten für jeweils einen Treffer enthalten.

Hier ein einfaches Beispiel:

if (preg_match_all('/<title>/i', $html, $alle_treffer) > 1)
  print "Hoppla, die Webseite hat mehrere <title>-Tags!\n";

Bei preg_match_all ist das dritte Argument – die Variable, in die die Trefferdaten geschrieben werden – obligatorisch, anders als bei preg_match. Deshalb taucht in diesem Beispiel die Variable $alle_treffer auf, obwohl sie nicht weiter verwendet wird.


Ein leerer Treffer oder ein Nicht-Treffer

preg_match gibt im $treffer-Array den leeren String zurück, wenn das entsprechende Klammerpaar nicht Teil des Treffers ist (mit der schon mehrfach erwähnten Einschränkung, dass leere Elemente am Ende des $treffer-Arrays komplett abgeschnitten werden). Ein Unterausdruck kann je nachdem aber auch auf den leeren String im Suchstring passen. Ich würde es daher begrüßen, wenn der Rückgabewert im ersten Fall NULL wäre.

Deshalb habe ich eine Variante von preg_match entwickelt, die sich genau so verhält (ich nenne sie reg_match). Sie führt die eigentliche Mustersuche mit PREG_OFFSET_CAPTURE aus und bekommt die ausführlichen Informationen über die von den Klammerausdrücken aufgefangenen Teilstrings. Mit diesen Informationen wird das $treffer-Array mit NULL-Werten an den entsprechenden Elementen aufgebaut:

function reg_match($regex, $suchstring, &$treffer, $offset = 0)
{
    $resultat = preg_match($regex, $suchstring, $treffer,
                           PREG_OFFSET_CAPTURE, $offset);
    if ($resultat) {
        $f = create_function('&$X', '$X = $X[1] < 0 ?  NULL : $X[0];');
        array_walk($treffer, $f);
    }
    return $resultat;
}

Das $treffer-Array aus dem Aufruf von reg_match entspricht exakt dem des ursprünglichen preg_match ohne das PREG_OFFSET_CAPTURE-Flag, aber nun sind alle Elemente auf NULL gesetzt, die einem Klammerausdruck entsprechen, der nicht am Gesamttreffer teilnimmt.


Trefferdaten sammeln

Ein anderer Unterschied zu preg_match – der entscheidende – betrifft die Art, wie die Trefferdaten zurückgegeben werden. Bei preg_match wird nur genau eine Mustersuche ausgeführt, deshalb enthält die $treffer-Variable auch nur Daten zu einem Treffer. Bei preg_match_all aber wird nach einem erfolgreichen Treffer immer wieder weitergesucht, und die Trefferdaten von allen diesen Treffern werden in der Variable abgelegt. Ich betone das, indem ich hier statt $treffer den Variablennamen $alle_treffer verwende, wenn es um preg_match_all geht.

Man kann von preg_match_all verlangen, die Trefferdaten in $alle_treffer in zwei möglichen Reihenfolgen anzuordnen. Dies geschieht mit einem der zwei Flags PREG_PATTERN_ORDER oder PREG_SET_ORDER.

Die Reihenfolge PREG_PATTERN_ORDER (Voreinstellung)

Im folgenden Beispiel wird die Reihenfolge PREG_PATTERN_ORDER verwendet (die ich im Folgenden »Aufgereiht« nenne, mehr dazu gleich). Dies ist auch die voreingestellte Reihenfolge, und im Beispiel wird auch kein explizites viertes Argument angegeben.

$suchstring = "
Hans A. Schmid
Lisa B. Meier";

/* Kein viertes Argument, also Voreinstellung PREG_PATTERN_ORDER */
preg_match_all('/^(\w+) (\w\.) (\w+)$/m', $suchstring, $alle_treffer);

Das erzeugt ein $alle_treffer-Array mit folgendem Inhalt:

array
(
  /* $alle_treffer[0] ist ein Array mit allen Gesamttreffern */
  0 => array ( 0 => "Hans A. Schmid",  /* Gesamter Treffertext des ersten Treffers */
               1 => "Lisa B. Meier"    /* Gesamter Treffertext des zweiten Treffers */ ),

  /* $alle_treffer[1] ist ein Array, das alle vom 1. Klammerpaar eingefangenen Texte enthält */
  1 => array ( 0 => "Hans",  /* 1. Klammertext aus dem ersten Treffer */
               1 => "Lisa"   /* 1. Klammertext aus dem zweiten Treffer */ ),

  /* $alle_treffer[2] ist ein Array, das alle vom 2. Klammerpaar eingefangenen Texte enthält */
  2 => array ( 0 => "A.",    /* 2. Klammertext aus dem ersten Treffer */
               1 => "B."     /* 2. Klammertext aus dem zweiten Treffer */ ),

  /* $alle_treffer[3] ist ein Array, das alle vom 3. Klammerpaar eingefangenen Texte enthält */
  3 => array ( 0 => "Schmid", /* 3. Klammertext aus dem ersten Treffer */
               1 => "Meier"   /* 3. Klammertext aus dem zweiten Treffer */ )
)

Es wurden zwei Treffer gefunden, und von beiden gibt es einen »gesamten Treffertext« und drei Strings, die von den drei Klammerpaaren eingefangen wurden. Ich nenne diese Reihenfolge »Aufgereiht«, weil die Gesamttreffer in einem Array abgelegt werden (in $alle_treffer[0]), alle Untertreffer aus dem ersten Klammerpaar in einem anderen ($alle_treffer[1]) usw.

Diese voreingestellte Ordnung kann man mit dem Flag PREG_SET_ORDER ändern.

Die Reihenfolge PREG_SET_ORDER

Die andere Möglichkeit für die Reihenfolge der Trefferwerte nenne ich hier »Gestapelt«, sie wird mit dem Flag PREG_SET_ORDER aktiviert. Alle Resultate aus dem ersten Treffer werden in $alle_treffer[0] abgelegt, die aus dem zweiten Treffer in $alle_treffer[1] usw. Es ist so, als ob man den Suchstring mit preg_match »zu Fuß« abklappern würde und bei jedem Treffer die Trefferdaten an das $alle_treffer-Array anhängen würde.

Hier folgt die PREG_SET_ORDER-Version des vorherigen Beispiels:

$suchstring = "
Hans A. Schmid
Lisa B. Meier";

preg_match_all('/^(\w+) (\w\.) (\w+)$/m', $suchstring, $alle_treffer, PREG_SET_ORDER);

Sie ergibt ein $alle_treffer-Array mit den folgenden Werten:

array
(
  /* $alle_treffer[0] ist ganz analog zu $treffer bei preg_match */
  0 => array ( 0 => "Hans A. Schmid", /* Gesamter Treffertext aus dem ersten Treffer */
               1 => "Hans",           /* Text vom 1. Klammerpaar des ersten Treffers */
               2 => "A.",             /* Text vom 2. Klammerpaar des ersten Treffers */
               3 => "Schmid"          /* Text vom 3. Klammerpaar des ersten Treffers */ ),

  /* Auch $alle_treffer[1] ist ganz analog zu $treffer bei preg_match */
  1 => array ( 0 => "Lisa B. Meier",  /* Gesamter Treffertext aus dem zweiten Treffer */
               1 => "Lisa",           /* Text vom 1. Klammerpaar  des zweiten Treffers */
               2 => "B.",             /* Text vom 2. Klammerpaar  des zweiten Treffers */
               3 => "Meier"           /* Text vom 3. Klammerpaar  des zweiten Treffers */ )
)

Hier noch eine kleine Zusammenfassung zu den beiden Resultat-Reihenfolgen:

Reihenfolge Flag Beschreibung und Beispiel
Aufgereiht PREG_PATTERN_ORDER Entsprechende Teilresultate von jedem Treffer werden zusammen gruppiert.
$alle_treffer[$klammer_num][$treffer_num]
Gestapelt PREG_SET_ORDER Alle Daten aus einem Treffer werden zusammen gruppiert.
$alle_treffer[$treffer_num][$klammer_num]

Das PREG_OFFSET_CAPTURE-Flag bei preg_match_all

Das Flag PREG_OFFSET_CAPTURE kann man bei preg_match_all auf die gleiche Art verwenden wie bei preg_match. Dann werden alle Strings im $alle_treffer-Array zu kleinen Arrays mit zwei Elementen, die den eigentlichen Treffertest und auch den Byte-Offset vom Anfang des Suchstrings aus enthalten. Die Variable $alle_treffer ist dann also ein Array von Arrays von Arrays, ganz schön kompliziert. Wenn man sowohl PREG_OFFSET_CAPTURE als auch PREG_SET_ORDER verwenden will, kann man beide mit einem binären »Oder« verknüpfen:

preg_match_all($pattern, $suchstring, $alle_treffer,
               PREG_OFFSET_CAPTURE | PREG_SET_ORDER);

preg_match_all mit benannten Unterausdrücken

Wenn man benannte Klammerausdrücke verwendet, werden in $alle_treffer zusätzliche Elemente mit den Namen als Indizes eingefügt (wie bei preg_match). Nach

$suchstring = "
Hans A. Schmid
Lisa B. Meier";
/* Kein viertes Argument, also Voreinstellung PREG_PATTERN_ORDER */
preg_match_all('/^(?P<Vorname>\w+) (?P<Initial>\w\.) (?P<Famname>\w+)$/m',
               $suchstring, $alle_treffer);

enthält $alle_treffer die Werte:

array
(
    0         => array ( 0 => "Hans A. Schmid",  1 => "Lisa B. Meier" ),
    "Vorname" => array ( 0 => "Hans",     1 => "Lisa"   ),
    1         => array ( 0 => "Hans",     1 => "Lisa"   ),
    "Initial" => array ( 0 => "A.",       1 => "B."     ),
    2         => array ( 0 => "A.",       1 => "B."     ),
    "Famname" => array ( 0 => "Schmid",   1 => "Meier" ),
    3         => array ( 0 => "Schmid",   1 => "Meier" )
)

Das gleiche Beispiel mit PREG_SET_ORDER

preg_match_all('/^(?P<Vorname>\w+) (?P<Initial>\w\.) (?P<Famname>\w+)$/m',
               $suchstring, $alle_treffer, PREG_SET_ORDER);

ergibt ein $alle_treffer-Array mit den Werten:

array
(
    0 => array ( 0       => "Hans A. Schmid",
                 Vorname => "Hans",
                 1       => "Hans",
                 Initial => "A.",
                 2       => "A.",
                 Famname => "Schmid",
                 3       => "Schmid" ),
    1 => array ( 0       => "Lisa B. Meier",
                 Vorname => "Lisa",
                 1       => "Lisa",
                 Initial => "B.",
                 2       => "B.",
                 Famname => "Meier",
                 3       => "Meier" )
)

Ich persönlich würde es begrüßen, wenn bei der Verwendung von benannten Unterausdrücken die »numerischen« Elemente weggelassen würden; das wäre sauberer und platzsparender. Aber auch wenn sie nun einmal vorhanden sind, heißt das nicht, dass man sie verwenden muss.

  

<< 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