Grosses preg_replace - Problem...
Dieter
- php
Hallo,
leider komme ich mit meinem bescheidenen Regexp-Kenntnissen seit Tagen nicht weiter.
Ich möchte einen Stringbereich zwischen zwei Begrenzungswerten (in diesem Fall doppelte eckige Klammern) austauschen, sowie den ursprünglichen Inhalt in einem Array speichern.
Da sich der Inhalt (der auszutauschende) noch durch eine Regel (Trennzeichen "|") unterscheiden kann, wollte ich zwei nachfolgend arbeitende Pattern verwenden, scheitere aber
leider schon an der Regel:
$pfad = "PFAD";
$pattern = '/[[(.*)|(.*)]]/eisU';
$ersatz = '<a href="'.$pfad.'/\1">\2</a>';
$string = preg_replace($pattern, $ersatz, $string);
Folgendes Ergebnis soll rauskommen:
Originalstring: Das ist ein Testsatz. Dieser Testsatz ist in einem [[String]] gespeichert und soll via preg_replace [[Arbeit|bearbeitet]] werden - leider sind aber meine bisherigen [[Bemühung|Bemühungen]] recht erfolglos.
Ergebnis: Das ist ein Testsatz. Dieser Testsatz ist in einem <a href="PFAD/String">String</a> gespeichert und soll via preg_replace <a href="PFAD/Arbeit">bearbeitet</a> werden - leider sind aber meine bisherigen <a href="PFAD/Bemühung">Bemühungen</a> recht erfolglos.
Nur leider bekomme ich das nicht hin. Mit dem Suchmuster bekomme ich folgendes Ergebnis:
Das ist ein Testsatz. Dieser Testsatz ist in einem <a href="PFAD/String]] gespeichert und soll via preg_replace [[Arbeit">bearbeitet</a> werden - leider sind aber meine bisherigen <a href="PFAD/Bemühung">Bemühungen</a> recht erfolglos.
Ich komme einfach nicht weiter und möchte daher um eure Mithilfe bitten:
a) wo ist mein Fehler bzw. was muß ich an der regexp verändern (bitte wenn möglich auch mit Erklährung, damit ich auch verstehe was falsch gelaufen ist)
b) wie kann ich zudem den Wert "\1" in ein Array bekommen ?
MfG
Dieter
n'abend,
$pfad = "PFAD";
$pattern = '/[[(.*)|(.*)]]/eisU';
du erwartest »[[etwas|anderes]]«, den Fall »[[etwas]]« hast du hier gar nicht berücksichtigt. Das ist auch der Grund, warum dein Test misslingt. Siehe unten
Schau dir bitte die Pattern Modifiers noch mal genauer an.
»e« bedeutet, dass der replace-string als PHP interpretiert werden soll - willst du das hier wirklich?
»s« bedeutet, dass auch Zeilenumbrüche durch . (DOT_ALL) erkannt werden sollen - willst du das hier wirklich? Kann / darf es Zeilenumbrüche in deinen [[blubb]] Tags geben?
»U« macht an dieser Stelle mehr oder weniger Sinn. UNGREEDY bewirkt, dass DOT_ALL nur soviel liest, wie es minimal lesen kann.
$ersatz = '<a href="'.$pfad.'/\1">\2</a>';
hast du dir durchgelesen, wie sich das mit dem Replace-String in preg_replace() so verhält?
$string = preg_replace($pattern, $ersatz, $string);
Originalstring: Das ist ein Testsatz. Dieser Testsatz ist in einem [[String]] gespeichert und soll via preg_replace [[Arbeit|bearbeitet]] werden - leider sind aber meine bisherigen [[Bemühung|Bemühungen]] recht erfolglos.
Dein RegeExp matcht hier (zufällig). Aber nicht ganz so, wie du dir das vorstellst:
/[[(.*)|(.*)]]/
\0 ist dabei »[[String]] gespeichert und soll via preg_replace [[Arbeit|bearbeitet]]«
\1 ist dabei »String]] gespeichert und soll via preg_replace [[Arbeit«
\2 ist dabei »bearbeitet«
Ich komme einfach nicht weiter und möchte daher um eure Mithilfe bitten:
a) wo ist mein Fehler bzw. was muß ich an der regexp verändern (bitte wenn möglich auch mit Erklährung, damit ich auch verstehe was falsch gelaufen ist)
Du kannst Bereiche mit einem ? optional machen:
/[[(.*)(|(.*))?]]/ -- achtung, die Indexe in deinem Replace-String werden sich hierbei ändern.
Der Punkt-Operator (DOT_ALL) matcht jedes Zeichen. Willst du nicht vielleicht nur Buchstaben (und ggf. Zahlen) matchen? Etwa so:
/[[([a-zA-Z0-9]+)(|([a-zA-Z0-9]+))?]]/
Du kannst auch, wenn du nicht mit einer Callback-Funktion arbeiten willst (was du aber wegen unterschiedlicher Indexe vermutlich machen willst) zwei separate RegExps benutzen, einer der [[string]] matcht und einer der [[string|string2]] matcht.
b) wie kann ich zudem den Wert "\1" in ein Array bekommen ?
du könntest dir mal preg_replace_callback() anschauen.
weiterhin schönen abend...
(Hallo|Hi(ho)|Tag) Dieter,
Ich möchte einen Stringbereich zwischen zwei Begrenzungswerten (in diesem Fall doppelte eckige Klammern) austauschen, sowie den ursprünglichen Inhalt in einem Array speichern.
Das sieht auf den ersten Blick wie Links in MediaWiki-Quellcodes aus. Also
könnte es sich lohnen mal im MediaWiki-Quellcode nachzuschauen. Irgendwo
dort sollte sowohl der passende RegEx, als auch die dazugehörige
Ersetzungsstrategie zu finden sein. ;-)
Naja, ich mal schnell was zusammengestrickt, was zumindest mit
deinem Testsatz zurechtkommt:
$haystack = 'Das ist ein Testsatz. Dieser Testsatz ist in einem [[String]]
gespeichert und soll via preg_replace [[Arbeit|bearbeitet]] werden -- leider
sind aber meine bisherigen [[Bemühung|Bemühungen]] recht erfolglos.';
$pfad = 'PFAD/';
$pcre = '/\[\[([^\x7c\]]+)(\x7c(.+))?\]\]/e';
$rpl = 'sprintf(
\'{a href="' . $pfad . '%s"}%s{/a}\',
"$1",
( "$3" ? "$3" : "$1" )
)';
$out = preg_replace($pcre, $rpl, $haystack);
a) wo ist mein Fehler bzw. was muß ich an der regexp verändern (bitte wenn möglich auch mit Erklährung, damit ich auch verstehe was falsch gelaufen ist)
Das Suchmuster $pcre besteht aus den umgebenden doppelten eckigen Klammern.
Darin befinden sich zwei Subpattern in runden Klammern. Das erste
"([^\x7c]]+)" passt auf mindestens ein oder eine beliebige Zahl von
Zeichen, die nicht der vertikale Trennstrich "\x7c" und auch nicht eine
schließende eckige Klammer "]" darstellen.
Das zweite "(\x7c(.+))?" passt auf die Zeichenfolge "|" plus ein oder
mehrere beliebige Zeichen. Das Muster "(.+)" ist als drittes
Subpattern nötig, weil wir ja beim Ersetzen nur den Text nach dem
senkrechten Strich brauchen.
Der Modifikator "/e" sorgt dafür, dass der Ersetzen-String $rpl als
PHP-Code ausgeführt wird. Das ist nötig, da wir ja eine Fallunterscheidung
treffen müssen: Ist das Subpattern Nummer 3 gefunden worden, wird
es im Ausgabe-String eingesetzt, wenn nicht, übernimmt Subpattern 1
diese Aufgabe.
Man könnte den Ersetzen-String auch anders basteln. Ich habe für die
Fallunterscheidung den Konditionaloperator
("is_wahr ? dann_nimm_meinen_wert : nö,_dann_nimm_meinen_wert")
und sprintf() missbraucht. Glücklicherweise wertet PHP leere Zeichenketten als
"nicht TRUE", deshalb gibt
( "$3" ? "$3" : "$1" )
den Wert von "$1" zurück, wenn das Subpattern 3 nicht gepasst hat.
Das Ganze ist eher als Prototyp zu verstehen. Vor allem solltest du das
Verhalten beim Auftreten der Zeichen "|", "[", "]" und eventuell noch
anderer für dein Script bedeutsamer Steuerzeichen testen, damit nicht
"kaputte Pfadangaben" nach der Ersetzung herauskommen. Im Moment frisst
das Suchmuster auch Zeichenketten, die über mehrere Zeilen gehen. Am
besten machst du dir erstmal klar, welche Zeichen im Pfadbestandteil
überhaupt erlaubt sind. Eventuell müssen sie auch noch URL-enkodiert
werden.
Auch kann man mit preg_replace_callback und einer anonymen Funktion
übersichtlicheren Quellcode und ein schnelleres Script produzieren.
b) wie kann ich zudem den Wert "\1" in ein Array bekommen ?
Das ist schwierig. Mit preg_replace_callback() kommst du zwar an den
Wert, aber du kannst ihn nur über eine globale Hilfs-Variable an das Script
weitergeben. Sauberer wäre aus meiner Sicht eine Lösung mit preg-match(),
dessen Offset-Parameter und PREG_OFFSET_CAPTURE in einer Schleife.
... oder du teilst mit [preg_split() den Text in ein Array aus gewöhnlichen
Textstücken und Links, ersetzt dann alle Links durch den passenden
HTML-Quelltext und schraubst das Array am Schluss wieder zu einer
Zeichenkette zusammen.
MffG
EisFuX
(Hallo|Hi(ho)|Tag) EisFuX,
b) wie kann ich zudem den Wert "\1" in ein Array bekommen ?
Das ist schwierig. Mit preg_replace_callback() kommst du zwar an den
Wert, aber du kannst ihn nur über eine globale Hilfs-Variable an das Script
weitergeben.
Was nicht ganz stimmt, das gilt nur für anonyme Funktionen, also die, die mit
create_function() erzeugt wurden.
Du kannst aber preg_replace_callback() auch eine beliebige selbstdefinierte
Funktion oder eine Methode eines Objekts übergeben.
MffG
EisFuX