preg_replace Problem
mixmastertobsi
- php
0 dedlfix0 mixmastertobsi1 mermshaus0 dedlfix
0 pl0 Gunnar Bittersmann0 mixmastertobsi0 Rolf b
Hallo,
ich scheiter gerade an einer regular expression Anweisung.
Der Text ist wie folgt.
TEXTTEXT {{ ZITAT 1 {{ ZITAT 2 }} }} ENDE TEXT
Nun soll das zwischen {{ und }} in einen DIV-Container gepackt werden.
Meine Anweisung war wie folgt
preg_replace("/{{([^\}\{]*)}}/i","<div class=\"quote\">$1</div>",$text);
aber ich bekomme nur einmal die "Klammer" ersetzt
TEXTTEXT {{ ZITAT 1 <div class="quote"> ZITAT 2 </div> }} ENDE TEXT
eiegntlich sollte es so ausehen.
TEXTTEXT <div class="quote"> ZITAT 1 <div class="quote"> ZITAT 2 </div> </div> ENDE TEXT
Wenn ich die Ausgabe zweimal hintereinander mache, klappt es, aber das kann ja nicht die Lösung sein, denn theoretisch könnte der Text 10 solcher Klammern haben - also brauche ich 10 mal diese replace Anweisung
$text = preg_replace("/{{([^\}\{]*)}}/i","<div class=\"quote\">$1</div>",$text);
$text = preg_replace("/{{([^\}\{]*)}}/i","<div class=\"quote\">$1</div>",$text);
Tach!
TEXTTEXT {{ ZITAT 1 {{ ZITAT 2 }} }} ENDE TEXT
Das sieht nach einem rekursiven Muster aus.
preg_replace("/{{([^\}\{]*)}}/i","<div class=\"quote\">$1</div>",$text);
Die geschweiften Klammern sind übrigens keine Sonderzeichen und müssen nicht maskiert werden.
dedlfix.
Danke DIR!
Kannst Du mal bitte ein Beispiel machen, wie es in meinem Beispiel aussehen müsste?
Tach!
Kannst Du mal bitte ein Beispiel machen, wie es in meinem Beispiel aussehen müsste?
Nee, kann ich nicht. Ich weiß nur, dass das dafür gedacht ist und hab es dir als Stichwort zur weiteren konkreten Recherche geliefert.
dedlfix.
Hallo mixmastertobsi,
Kannst Du mal bitte ein Beispiel machen, wie es in meinem Beispiel aussehen müsste?
Da du ja ersetzen möchtest (nicht nur die äußerste Ebene finden), wird preg_replace
mit einem rekursiven regex nicht ausreichen. Das rekursive Suchmuster ermöglicht es, die äusserste Ebene zu finden, es ist aber afaik nicht möglich, in jedem Selbstaufruf mit dem captured match zu ersetzen.
1.) Nun könntest du per preg_replace_callback
(siehe Beispiel) von aussen nach innen auflösen.
function parse_recursive($inp)
{
if (is_array($inp))
$inp = '<div class="quote">'.$inp[1].'</div>';
return preg_replace_callback('/{{((?>[^}{]+|(?R))*)}}/', 'parse_recursive', $inp);
}
Hier ein Beispiel auf eval.in und das Suchmuster auf regex101 zum probieren.
2.) Oder wie schon angesprochen per preg_replace
und while
von innen nach aussen.
$regx = '/{{([^}{]+)}}/'; $repl = '<div class="quote">$1</div>';
while(($tmp = preg_replace($regx, $repl, $str)) !== $str) $str = $tmp;
Noch ein Beispiel auf eval.in
Würde eher zu Variante 2 tendieren. Viel Erfolg, Robert
@mixmastertobsi @dedlfix
Ein Problem mit solchen Fragestellungen ist auch immer, dass man erst mal definieren müsste, was bei {{ foo }} bar }}
oder {{ foo {{ bar }}
passieren soll.
Tach!
Ein Problem mit solchen Fragestellungen ist auch immer, dass man erst mal definieren müsste, was bei
{{ foo }} bar }}
oder{{ foo {{ bar }}
passieren soll.
Garbage in, garbage out - wäre eine Strategie, den Verwender in die Pflicht zu nehmen, Ordentliches zu liefern, wenn er Ordentliches zurückhaben möchte. Wenn man solche Fehler finden möchte, wird man mit Regex nicht weiterkommen. Da muss dann ein Parser ran, der in der Lage ist, ungültige Syntax zu erkennen. Das ist letzlich eine Frage von Aufwand und Nutzen unter Berücksichtigung des Ziels, ob der einfache Regex reicht oder ob es ein komplexer Parser sein muss.
dedlfix.
@@dedlfix
Garbage in, garbage out - wäre eine Strategie
Oft ist Garbage in, error message out die bessere.
LLAP 🖖
Tach!
Garbage in, garbage out - wäre eine Strategie
Oft ist Garbage in, error message out die bessere.
Oft ist es besser, nicht ohne Anwendungsfall die Strategien zu bewerten (=> anwendungsfallorientiert statt strategieorientiert).
dedlfix.
Du suchst den Schnauzer? Oder das hier, damit gehts auch ;)
Du suchst den Schnauzer? Oder das hier, damit gehts auch ;)
Hast du die richtige Seite verlinkt? Ich sehe da spontan keinen wirklichen Zusammenhang zur Fragestellung. :)
@@mixmastertobsi
TEXTTEXT {{ ZITAT 1 {{ ZITAT 2 }} }} ENDE TEXT
Nun soll das zwischen {{ und }} in einen DIV-Container gepackt werden.
Ich sehe da zwei Fehler:
div
? Für Zitate ist blockquote
(Block) bzw. q
(Inline) da.
Ein regulärer Ausdruck ist für solch einen Parser das falsche Werkzeug.
LLAP 🖖
Hallo,
das DIV war ja nur ein Beispiel. Kann gerne auch blockquote sein.
Aber warum ist hier ein regulärer Ausdruck der falsche ANsatz?
Würde mich auch interessieren, welche fertige Library dafür geeignet ist. Wenn man es ohne Regex mit der Hand programmiert, wird es sicherlich fixer, aber auf jeden Fall ist der Code dann länger.
Deine Regex funktioniert aber schlecht. Sollte
TEXTTEXT {{ ZITAT 1 { ZITAT FORTSETZUNG }} ENDE TEXT
nicht
TEXTTEXT <q> ZITAT 1 { ZITAT FORTSETZUNG </q> ENDE TEXT
ergeben? Tut es aber nicht.
Ich habe es mal mit einer lookahead-Assertion versucht. Das Pattern matcht zuerst ein {{ und macht die Assertion, dass darauf kein weiteres {{ folgt. Dann matcht es beliebige Zeichen, aber non-greedy, und ein }}. Der non-greedy Match sorgt dafür, dass .* bei einem geschachtelten Zitat das innere }} nicht einschließt.
Das ganze legst Du in eine Schleife und machst es so lange, wie etwas ersetzt wird. Die Spielerei mit $i soll nur anzeigen, in welcher Reihenfolge er die Zitate ersetzt. Das musst Du natürlich für den realen Einsatz entfernen.
$text = "TEXTTEXT {{ ZITAT 1 {{ ZITAT 2 }} Z1 }} {{ ENDE }} TEXT";
$i=1;
while (true) {
$textNeu = preg_replace("//{{(?!.*{{)(.*?)}}/i","<q_$i>$1</q_$i>",$text);
if ($text === $textNeu) break;
$text = $textNeu;
$i++;
}
echo $text;
Eine Lösung ohne Regex wäre wohl ein handgemachter Parser, der von links nach rechts durchgeht und einen Quote-Stack bildet, oder die Nutzung einer PHP Library, die ich nicht kenne.
Rolf