preg_replace

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

preg_replace(pattern, ersatztext, suchstring [, limit [, anzahl ]])

Parameter

pattern

Der Pattern-Parameter: eine Regex mit Begrenzungszeichen, mit optionalen Modifikatoren. pattern kann auch ein Array sein, dessen Elemente Pattern-Strings sind.

ersatztext

Der Ersatztext-String oder ein Array von Ersatztext-Strings, wenn pattern ein Array ist. Wenn der e-Modifikator verwendet wird, werden diese Strings als PHP-Anweisungen interpretiert (siehe unter Einfaches preg_replace mit je einem Pattern, Ersatztext und Suchstring).

suchstring

Der Text, der abgesucht werden soll. Auch hier ist ein Array von Suchstrings zulässig, diese werden der Reihe nach abgearbeitet.

limit

Eine optionale Zahl, die angibt, wie viele Ersetzungen maximal vorgenommen werden sollen (siehe unter Einfaches preg_replace mit je einem Pattern, Ersatztext und Suchstring).

anzahl

Eine optionale Variable, in der die Anzahl der tatsächlich erfolgten Ersetzungen zurückgegeben wird (erst ab PHP 5, siehe unter Einfaches preg_replace mit je einem Pattern, Ersatztext und Suchstring).

Rückgabewert

Wenn suchstring ein einfacher String ist, ist auch der Rückgabewert ein solcher (eine Kopie des suchstring mit den Ersetzungen). Wenn suchstring ein String-Array ist, wird ebenfalls ein String-Array zurückgegeben (dessen Elemente die modifizierten Versionen der suchstring-Elemente sind).

Erläuterungen

In PHP gibt es eine Anzahl von Möglichkeiten für das Suchen und Ersetzen. Wenn die Suchmuster bloße Strings sind, dann sind str_replace und str_ireplace die Methoden der Wahl, aber für komplexere Aufgaben nimmt man preg_replace.

Als typischen Anwendungsfall betrachten wir ein Ärgernis, das im Web öfter vorkommt: eine Eingabemaske für eine Telefon- oder Kreditkartennummer mit der Aufforderung »Keine Leerzeichen oder Striche!«. Man wird gezwungen, eine lange Ziffernreihe ohne irgendwelche Strukturierungen einzugeben. (Anmerkungen: Offenbar gibt es im Web sehr viele faule Programmierer. Mein Bruder hat solche Webseiten in einer »Hall of Shame« gesammelt.) Dabei wäre es für den Programmierer so einfach, dem Benutzer diese kleine Arbeit abzunehmen:

$karten_nummer = preg_replace('/\D+/', '', $karten_nummer);
/* $karten_nummer enthält jetzt nur Ziffern oder ist leer */

Hier werden mit preg_replace alle Nicht-Zifferzeichen entfernt. Etwas ausführlicher gesagt: preg_replace kopiert den Inhalt von $karten_nummer und ersetzt dabei alle Folgen von Nicht-Ziffern durch gar nichts, d.h. durch den leeren String. Das Resultat wird zum neuen Wert von $karten_nummer.

Einfaches preg_replace mit je einem Pattern, Ersatztext und Suchstring

Für die ersten drei Parameter (pattern, ersatztext und suchstring) kann wahlweise ein String oder aber ein Array von Strings als Argument angegeben werden. Im häufigsten Fall sind alle drei einfache Strings; preg_replace erzeugt eine Kopie des Suchstrings, startet die Mustersuche, ersetzt den Treffer durch den Ersatztext und wiederholt die letzten zwei Schritte, bis die Mustersuche fehlschlägt.

Im Ersatztext bezieht sich ›$0‹ auf den vollen Treffertext der gerade erfolgten Mustersuche, ›$1‹ auf den Text, der dabei vom ersten Klammerpaar eingefangen wurde, ›$2‹ auf den des zweiten Klammerpaars usw. Diese Dollar-Ziffer-Kombinationen sind keine eigentlichen Variablen wie in anderen Programmiersprachen, es ist eine bloße Notation, die preg_replace anweist, den Ersatztext entsprechend vorzubehandeln. Man kann um die Zahl auch geschweifte Klammern schreiben, also beispielsweise ›${0}‹ oder ›${1}‹; das ist wichtig, wenn auf die Dollar-Ziffer-Kombination eine weitere Ziffer folgt.

Im folgenden Programmbeispiel werden großgeschriebene Wörter in einem HTML-Text zusätzlich mit <b>-Tags hervorgehoben:

$html = preg_replace('/\b[A-Z]{2,}\b/', '<b>$0</b>', $html);

Mit dem e-Modifikator (der nur bei preg_replace zulässig ist) wird der Ersatztext als PHP-Code interpretiert. Dieser Code wird nach jedem Treffer und nach dem Ersetzen von ${0}, ${1} usw. ausgeführt, das Resultat dieser PHP-Anweisung wird zum eigentlichen Ersatztext.

Wir erweitern das Beispiel oben und schreiben das mit <b> hervorgehobene Wort nun in Kleinbuchstaben:

$html = preg_replace('/\b[A-Z]{2,}\b/e',
                     'strtolower("<b>$0</b>")',
                     $html);

Wenn der gefundene Treffer beispielsweise ›HALLO‹ ist, wird dieses Wort statt dem ›$0‹ in den Ersatztext eingebaut; das ergibt den String ›strtolower("<b>HALLO</b>")‹. Dieser wird als PHP-Anweisung ausgeführt und liefert als neuen Ersatztext ›<b>hallo</b>‹.

Wenn der e-Modifikator verwendet wird, werden die Dollar-Ziffer-Kombinationen im Ersatztext auf eine ganz bestimmte Weise interpoliert: Anführungszeichen (Hochkommas und doppelte) werden dabei mit einem Backslash maskiert. Ohne diese Maßnahme würde ein Anführungszeichen im zu interpolierenden String zu ungültigem PHP-Code führen.

Im Zusammenhang mit dem e-Modifikator und ›$0‹, ›$1‹ usw. ist es fast immer ratsam, den Ersatztext in Hochkommas anzugeben; damit die Variableninterpolation nicht zum falschen Zeitpunkt stattfindet.

Dieses Beispiel erledigt etwa dasselbe wie die htmlspecialchars()-Funktion von PHP:

$ersatztext = array ('&' => '&amp;',
                     '<' => '&lt;',
                     '>' => '&gt;',
                     '"' => '&quot;');
$neuer_suchstring = preg_replace('/[&<">]/eS', '$ersatztext["$0"]', $suchstring);

In diesem Beispiel ist es sehr wichtig, dass der ersatztext in Hochkommas angegeben wird. So wird die $ersatztext-Variable erst dann interpoliert, wenn preg_replace den Ersatztext als PHP-Code ausführt. Mit doppelten Anführungszeichen wäre diese Variableninterpolation vorgenommen worden, bevor preg_replace überhaupt aufgerufen worden wäre.

Der S-Modifikator macht den Code hier etwas effizienter (siehe Der S-Modifikator).

Wenn als viertes Argument von preg_replace ein oberes Limit angegeben wird, werden nur maximal so viele Suchen-und-Ersetzen-Vorgänge ausgeführt (jeweils pro Regex und pro Suchstring, siehe den nächsten Abschnitt). Der voreingestellte Wert ist -1, was »kein Limit« bedeutet.

Als fünftes Argument schließlich kann man (ab PHP 5) eine Variable angeben, in die die Anzahl der tatsächlich erfolgten Ersetzungen geschrieben wird. Wenn man wissen will, ob überhaupt Ersetzungen vorgenommen wurden, kann man die Strings vorher und nachher vergleichen, aber es ist deutlich effizienter, eine anzahl-Variable anzugeben und zu prüfen.

preg_replace mit mehreren Pattern, Suchstrings und Ersatztexten

In allen Beispielen aus den vorhergehenden Abschnitten war das suchstring-Argument ein einzelner String, und so ist es auch oft. Es kann allerdings auch ein String-Array sein; in diesem Fall wird der Suchen-und-Ersetzen-Vorgang in allen Strings ausgeführt, und der Rückgabewert ist ebenso ein Array von Strings.

Unabhängig vom Suchstring kann man auch für die Parameter pattern und ersatztext statt einem String ein Array von Strings angeben. In der folgenden kleinen Tabelle sind die möglichen Paarungen und ihre Wirkung aufgeführt:

Pattern Ersatztext Wirkung
String String Pattern anwenden und jeden Treffer durch den Ersatztext ersetzen
Array String Der Reihe nach jedes Pattern anwenden und jeden Treffer durch den Ersatztext ersetzen
Array Array Der Reihe nach jedes Pattern anwenden und jeden Treffer durch den entsprechenden Ersatztext ersetzen
String Array (nicht erlaubt)

Wenn das suchstring-Argument ein Array ist, wird auch hier die Ersetzung bei jedem enthaltenen String vorgenommen, und das Resultat ist ebenso ein String-Array.

Beachten Sie, dass sich der limit-Parameter auf jeweils ein Pattern und einen Suchstring bezieht, nicht auf alle zusammen. Die Anzahl der vorgenommenen Ersetzungen, die im anzahl-Parameter zurückgegeben wird, ist dagegen die totale, über alle Pattern und Suchstrings summierte.

Im folgenden Beispiel sind sowohl pattern als auch ersatztext Arrays. Das Programm bewirkt etwa das Gleiche wie die eingebaute PHP-Funktion htmlspecialchars(), es ersetzt bestimmte Sonderzeichen, so dass sie gefahrlos in HTML verwendet werden können:

$html = preg_replace(
          array('/&/',   '/</',  '/>/',  '/"/'   ), /* Nach diesen suchen ...  */
          array('&amp;', '&lt;', '&gt;', '&quot;'), /* ... durch diese ersetzen ... */
          $text                                     /* ... in einer Kopie hiervon */
);

Einen $text von beispielsweise

AT&T --> "baby Bells"

verwandelt das Programm in:

AT&amp;T --&gt; &quot;baby Bells&quot;

Man kann natürlich auch vorher aufgebaute Array-Variablen verwenden. Die folgende Version arbeitet genau gleich und produziert auch die gleichen Resultate:

$pattern     = array('/&/',   '/</',  '/>/',  '/"/'   );
$ersatztexte = array('&amp;', '&lt;', '&gt;', '&quot;');

$html = preg_replace($pattern, $ersatztexte, $text);

Es ist ganz praktisch, dass man bei preg_replace Arrays als Argumente verwenden kann (man braucht die Schleifen für jedes Pattern und jeden Suchstring nicht explizit auszuprogrammieren), aber eigentlich wird damit die Funktionalität nicht erweitert. Die Pattern werden beispielsweise nicht parallel abgearbeitet. Immerhin werden die eingebauten Schleifen effizienter sein als in PHP ausprogrammierte, und der Code ist auch etwas lesbarer.

Zur Illustration ein Beispiel, in dem alle drei Argumente Arrays sind:

$resultat_array = preg_replace($regex_array, $ersatztext_array, $suchstring_array);

Dies entspricht etwa folgendem PHP-Code:

$resultat_array = array();
foreach ($suchstring_array as $suchstring)
{
   reset($regex_array);      // Diese zwei Arrays werden in der internen
   reset($ersatztext_array); // Reihenfolge von PHP abgearbeitet.
   while (list(,$regex) = each($regex_array))
   {
       list(,$ersatztext) = each($ersatztext_array);
       // Regex und Ersatztext sind vorhanden, auf einen einzelnen Suchstring anwenden ...
       $suchstring = preg_replace($regex, $ersatztext, $suchstring);
   }
  
   // Suchstring wurde von allen Regex bearbeitet, der veränderte Text wird jetzt an das ...
   $resultat_array[] = $suchstring;  // ... Resultat-Array angehängt.
}

Reihenfolge der Elemente im Array

Wenn pattern und ersatztext Arrays sind, werden sie in der »natürlichen« Reihenfolge von PHP abgearbeitet; das ist im Allgemeinen die Reihenfolge, in der die Elemente dem Array hinzugefügt wurden (das dem Array pattern zuerst hinzugefügte Element wird mit dem gepaart, das zuerst im Array ersatztext vorhanden war, usw.). Das funktioniert bei »literalen Arrays«, also solchen, die mit dem array()-Konstruktor erzeugt wurden, ohne irgendwelche Probleme:

$suchstring = "Dieser Satz hat 8 Wörter und 39 Zeichen";

$resultat = preg_replace(array('/[a-zäöüß]+/i', '/\d+/'),
                         array('Wort<$0>',      'Zahl<$0>'),
                         $suchstring);

print "$resultat\n";

Die Regex ˹[a-zäöüß]+˼ wird mit ›Wort<$0>‹ gepaart und ˹\d+˼ mit ›Zahl<$0>‹, wir erhalten also:

Wort<dieser> Wort<satz> Wort<hat> Zahl<8> Wort<wörter> Wort<und> Zahl<39> Wort<zeichen>

Wenn aber pattern oder ersatztext Arrays sind, die im Programmverlauf stückweise zusammengesetzt werden, kann es sein, dass diese interne Reihenfolge von der scheinbaren (nämlich der durch die numerischen Indexwerte gegebenen) abweicht. Deshalb wird im ausprogrammierten Beispiel aus dem vorherigen Abschnitt each verwendet, damit sichergestellt ist, dass die Elemente der Arrays in der internen Reihenfolge abgearbeitet werden, unabhängig vom numerischen Wert der Schlüssel.

Wenn nicht bekannt ist, ob die Arrays pattern und ersatztext nach demselben Schema aufgebaut wurden und deshalb möglicherweise eine unterschiedliche interne Reihenfolge haben, ist es am sinnvollsten, die Werte mit ksort() zu ordnen und damit sicherzustellen, dass beide Arrays in der gleichen, definierten Reihenfolge abgearbeitet werden.

Wenn sowohl pattern als auch ersatztext Arrays sind, das pattern-Array aber mehr Elemente enthält, werden für die fehlenden ersatztext-Elemente leere Strings angenommen.

Die Reihenfolge der regulären Ausdrücke im pattern-Array kann eine große Rolle spielen, weil sie in der gegebenen Reihenfolge nacheinander abgearbeitet werden. Im folgenden Programmbeispiel sind die regulären Ausdrücke und die Ersatzstrings in den zwei Arrays pattern und im entsprechenden ersatztext vertauscht.

$suchstring = "Dieser Satz hat 8 Wörter und 39 Zeichen";

$resultat = preg_replace(array('/\d+/',    '/[a-zäöüß]+/i'),
                         array('Zahl<$0>', 'Wort<$0>'     ),
                         $suchstring);
print "$resultat\n";

Ändert sich dadurch die Ausgabe? Zur Auflösung siehe Lösung 16.

  

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