Regex-Problem

scanline

Erfahrenes Mitglied
Hier eine Lösung die ich gefunden (und dann kopiert :-) ) habe, die ich aber nicht verstehe!

Ziel: Ich will in einem String alle Textstellen fett schreiben, die "block" heißen. Davon ausgenommen werden sollen Fundstellen innerhalb TAG's. Hier der kopierte und voll fnuktionstüchtige Lösungsansatz:

PHP:
$string='<blockquote> blockquote </blockquote>';
$search="block";   

$string=preg_replace("/((<[^>]*)|$search)/ei",'"\\1" == "\\2" ? "\\1" : "<b>\\1</b>"',$string);

echo $string;

(folge)richtige Ausgabe:
<blockquote> <b>blockquote</b> </blockquote>

Warum funktioniert das? Ich verstehe es nicht! Genauer gesagt den Pattern:
((<[^>]*)|$search)


Kann mir das mal einer erklären?

VG
MIcha
 
Hmm, du solltest dir das mal zu Gemüte führen.
Wenn du danach irgendetwas noch nicht verstehst, helf ich dir gerne weiter ;)

//EDIT: Alle Modifiers (in dem Fall ei) findest du hier.
 
Zuletzt bearbeitet:
Ich teil den Regex mal für dich in 2 Teile auf:
((<[^>]*) <- das ist der erste Pattern (#1)
| <- das steht für ein OR
$search) <- und das ist ein String (#2)
Das erste Suchmuster, öffnet eine Zeichenklasse (alles was in den echkigen Klammern steht), es matcht alle Zeichen ausser ">". Im Klartext sucht der Regex nach entweder einem HTML Tag oder nach dem String $search.
 
Eines kurz vorwech damit ich nicht unötige Bemühungen produziere...
Ich bin kein blutiger PHP- und auch kein regex-Anfänger. Die empfohlenen Tutorials kenne ich gut und auch einige andere.

teil den Regex mal für dich in 2 Teile auf:
((<[^>]*) <- das ist der erste Pattern (#1)
da geht das Verständnisproblem schon los...wie kann ein Pattern oder in diesem Fall auch ein Subpattern mit zwei Klammern geöffnet und mit einer geschlossen werden?
| <- das steht für ein OR
$search) <- und das ist ein String (#2)
...und diesem (Sub)Pattern fehlt eine Klammer! Warum funktioniert er trotzdem? Diese Schreibweise irritiert mich einfach.
Das erste Suchmuster, öffnet eine Zeichenklasse (alles was in den echkigen Klammern steht), es matcht alle Zeichen ausser ">".
er matcht bereits, wenn es mit der spitzen Klammer losgeht, aber egal...
Im Klartext sucht der Regex nach entweder einem HTML Tag oder nach dem String $search.
OK, Deine Erklärung mal etwas genauer unter die Lupe genommen...
Wenn er erst Muster I, dann Muster II auf Matches prüft passiert in angeführtem Beispiel doch folgendes:

Er trifft zuerst auf "<blockquote>, findet eine Übereinstimmung mit Muster I und speichert diese temporär als \1 ab. ABER, was matched er wirklich?
<[^>]* würde doch lediglich "<blockquote" matchen und als \1 abspeichern.

Weiter:
Muster II - Matched ebenfalls und speichert "block" als \2.

Ich mache mal direkt einen Sprung zum replace...
"\1"=="\2" ? "\1" : "<b>\1</b>"

Da \1 (<blockquote) nicht gleich \2 (block) ist, tritt else in Kraft und verwandelt
$search (block) in <b>block</b>, aber bereits im <blockquote>, da dort bereits der erste Match stattfand.
schreibt also: "<<blockquotequote>..."

Irgendwie noch nicht ganz rund die Sache...

Ich dachte lange ich hätte das Pattern verstanden bis ich versucht habe es wirklich zu verstehen :-)

VG
Micha
 
Original geschrieben von scanline
...und diesem (Sub)Pattern fehlt eine Klammer! Warum funktioniert er trotzdem? Diese Schreibweise irritiert mich einfach.
Da fehlt keine Klammer, da dieser Subpattern schon von einer Klammer umschlossen ist.

Original geschrieben von scanline
er matcht bereits, wenn es mit der spitzen Klammer losgeht, aber egal...
Ja macht er auch, hab ich was anderes geschrieben?
"<" ist nun mal kein ">", wenn ich also alle Zeichen ausser ">" schreibe ist klar, das "<" mitgematcht wird.

Original geschrieben von scanline <[^>]* würde doch lediglich "<blockquote" matchen und als \1 abspeichern.
exakt

Original geschrieben von scanline Weiter:
Muster II - Matched ebenfalls und speichert "block" als \2.
Wenn der erste Subpattern schon matcht, dann matcht der Zweite nicht.

Original geschrieben von scanline Da \1 (<blockquote) nicht gleich \2 (block) ist, tritt else in Kraft und verwandelt
$search (block) in <b>block</b>, aber bereits im <blockquote>, da dort bereits der erste Match stattfand.
schreibt also: "<<blockquotequote>..."
Wie gesagt, wenn der erste Matcht schon übereinstimmt, dann wird der zweite nicht weiter beachtet.
 
Wie gesagt, wenn der erste Matcht schon übereinstimmt, dann wird der zweite nicht weiter beachtet.
soweit klaer, das Verständnisproblem ist ein Türchen weiter:
wenn $1 matcht, kopiert er sich offensichtlich auch nach $2, sonst würde die nachstehende Bedingungsanweisung nicht funktionieren.

"\1" == "\2" ? "\1" : "<b>\1</b>"

Warum das?
...oder erstmal...
WOW, der Trick ist also dass sobald die Bedingung erstmalig erfüllt ist,
auch jeder weitere Subpattern automatisch den Wert des zuvor matchenden
Subpatterns erhält?!

Dann müsste (<[^>]*)|($search) dasselbe Ergebnis liefern.
Tut es aber nicht. Er liefert statt dessen $1=<blockquote, $2 nix
Wahrscheinlich weil ($search) jetzt nicht mehr Teil des Subpattern I ist,
oder?

Gut, verschachteln wir wieder die Klammern nochmal und drehen die Kiste mal um:
(<[^>]*|($search))
dasselbe Ergebnis: $1=<blockquote, $2 nix
Warum erbt $2 in diesem Fall nicht ebenfalls den Inhalt von $1?
Dann hängt es wohl doch nicht mit dem Umstand der Verschachtelung zusammen!?

Sorry, ich blicke das System noch nicht.
Warum also?
G
Micha
 
RegExp ist doch was Feines :-)

Hier mal mein Erläuterungsversuch:

In dem Ausdruck werden 2 Suchmuster gebildet... es fehlt auch keine Klammer, vererbt wird auch nichts.

Suchmuster\1 =>((<[^>]*)|$search)
Suchmuster\2 =>(<[^>]*)

Die Reihenfolge der Suchmuster ergibt sich aus den öffnenden, runden Klammern.

Das erste Suchmuster findet folglich einen Tag oder $search, wobei der Tag den Vorrang hat, weil er der erste der alternativen Ausdrücke ist.
Das 2. Suchmuster findet lediglich einen Tag.

Das Replacement:
"\1" == "\2"
erfragt ob der erste gefundene Suchausdruck mit dem zweiten identisch ist.
Dies ist nur der Fall, wenn es sich um einen Tag handelt, weil andernfalls \2 leer ist.

? "\1"
war diese Abfrage true, wird die Fundstelle durch sich selbst ersetzt... (\1 ist ja das komplette Suchmuster)

: "<b>\1</b>"
war diese Abfrage false, ist die Ersetzung <b>Fundstelle</b>

Hört sich eigentlich plausibel an :-)
 
Du bist der erste, der es mir so einleuchtend erklären konnte, somit unterstelle ich mal bist Du auch der erste, der diese Reckechse wirklich verstanden hat ;-)
Und ich bin damit der zweite :-)
Dank und Wink!
Micha
 
Zurück