wieder regex-problem
Felix Riesterer
- programmiertechnik
Liebe Selfer,
ich filtere HTML-Tags heraus, um sie durch eine eigene Form von BB-Code zu ersetzen. Das funktioniert auch ganz gut, nur bei einem speziellen Tag (<sup>) gibt es Schwierigkeiten. Bei <sup> wird _immer_ das erste öffnende und das letzte schließende Tag im Text ersetzt. Eventuell dazwischen vorhandene öffnende und schließende <sup>-Tags werden belassen. Hier mein Script (Ausschnitt):
// Schriftformatierungen umwandeln
$formatierungen = array("unterstrichen" => "u", "fett" => "b", "kursiv" => "i", "tiefgestellt" => "sub", "hochgestellt" => "sup");
foreach($formatierungen as $formatierung => $tag)
{
$suchmuster = '/(?i)<'.$tag.'>(.*)(?<!</'.$tag.'>)</'.$tag.'>/';
$ersetzung = '['.$formatierung.':\1]';
$quelltext = preg_replace($suchmuster, $ersetzung, $quelltext);
}
Dieses Script macht Folgendes: Aus "<b>fetter</b> Text" wird "[fett:fetter] Text". Aus "<i>kursiver</i> Text <i>mit</i> Anfang" wird "[kursiv:kursiver] Text [kursiv:mit] Anfang".
Aber: aus "Test-<sup>schrift</sup> mit zwei<sup>hoch 4</sup>" wird leider "Test-[hochgestellt:schrift</sup> mit zwei<sup>hoch 4]".
Warum? Meine Foreach-Schleife und mein Suchmuster funktionieren doch bei allen anderen Tags tadellos! Was ist an <sup> so anders?
Wer weiß Rat? Ich bin für jede Idee dankbar, denn ich komme hier einfach nicht weiter.
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Hallo,
$suchmuster = '/(?i)<'.$tag.'>(.*)(?<!</'.$tag.'>)</'.$tag.'>/';
Vielleicht wäre der Einsatz eines XML-Parsers o.ä. eher zu empfehlen. Deine RegExp hat als Schwachpunkt den Teil in der Mitte: (.*) ... diese Regel ist zu schwach um zu bestimmen, ob nun alles zwischen benachbarten sup-/sup-tags oder eben zwischen den entferntesten tags matchen soll. Bei drei solchen gruppen wäre theoretisch auch eine Match zwischen ersten sup und mittlerem /sup möglich.
Die Lookbehind-Assertion trifft ja nur zu, wenn nicht _unmittelbar_ vor dem </sup> ein zweites </sup> liegt. Dank des .* ist diese Assertion aber dann wieder unwichtig, wenn ein weiter entferntes </sup>-Tag diese Bedingung erfüllt.
Ich denke, regexps sind hier überfordert und man sollte gleich zu einem guten xml-parser greifen.
http://de.php.net/manual/de/ref.xml.php
ist grundlegend flink bei der Sache und abgesehen davon, dass er sich an nicht zusammengehörigen Tags (fehlenden Endtags) stört, ist er äußerst brauchbar.
Grüße, Thomas
Hi Thomas,
vielen Dank für Deine Antwort (und zu welcher Stunde!). Du hast mir sehr geholfen, da Du mir die exakte Funktionsweise meiner RegExp vor Augen geführt hast. Diese regulären Ausdrücke sind für mich noch ziemlich neu. Ich experimentiere mit ihnen mehr, als dass ich wirklich verstünde, was ich damit tue... Ich denke, ich sollte mich mit diesem Parser einmal beschäftigen.
Vielen Dank und gute Nacht!
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Liebe Selfer,
ich habe meine regex in den Griff bekommen:
$tag = "span";
$suchmuster = '/(?i)<'.$tag.'>((?:(?!</'.$tag.'>)(?:.))*)</'.$tag.'>/';
Dieser Ausdruck filtert alles zwischen einem öffnenden und einem schließenden Tag und stellt dabei sicher, dass dazwischen kein weiteres schließendes Tag desselben Typs steht. Damit werden zuverlässig Folgen von z.B. <span>...</span>.....<span>......</span>..<span>.................</span> erkannt, ohne eine Gruppe aus <span> und </span> auszulassen.
Anmerkung: Die Tags dürfen für dieses Suchmuster keinerlei Attribute (z.B. class="xyz") enthalten!
Liebe Grüße aus Ellwangen,
Felix Riesterer.