PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : php: Regex für listen


MadMan2k
2004-03-13, 22:41:01
wie müsste eigentlich ein Regex ausehen, der Listencodes, wie hier im Forum matchen soll?
Also mit unbestimmt vielen Subplattern.
Oder reicht hierfür preg_replace() nicht mehr aus?

Nase
2004-03-13, 23:08:25
Ganz spontan überlegt:

Der geschrieben Text mit dem BBCode wird in einer Variable gespeichert und an eine Funktion übergeben. In dieser Funktion läuft eine Schleife. Per preg_replace_callback wird nach den regulären Ausdrücken gesucht und bei Erfolg eine andere Funktion (callback) aufgerufen. Das Ergebnis von preg_replace_callback wird in einer weiteren Variablen untergrbracht und bei jedem Durchlauf der Schleife überprüft, ob der Code bei der Eingabe noch mit dem Code nach dem preg_replace_callback übereinstimmt, damit man ein Abbruchkriterium erreicht. Die Funktion, die beim Callback aufgerufen wird, ist eine einfache Switch-Anweisung. Je nachdem, was die erste Funktion an BBCodes findet, wird dieser hier durch den entsprechenden HTML-Code ersetzt. Bei Listen würde also bei Funktion eins nach [ list][ /list] gesucht. Der Switch würde dann so aussehen, dass alles, was zwischen [ list] und [ /list] steht zu den Werten anhand der [ *] Codes wird.
Hmmm, alles irgendwie ziemlich kompliziert ausgedrückt, aber ich hoffe du kannst damit etwas anfangen. Einen fertigen Code dafür habe ich leider nicht.

MadMan2k
2004-03-14, 14:05:50
thx, erstmal, aber ich habe da noch so meine Probleme den zweiten Regex zu formulieren, welcher die [ *] Matchen soll.

hier mein Code:


function liste($matches){
return '<ul>'.preg_replace('=\[\*\](.*)(\[\*\]|$)=Uis','<li>\1</li>', $matches[1]).'</ul>';
}

$str = "Hallo!\njetzt kommt ein Listentest:\n [ list] erster Eintrag zweiter Eintrag[/ list]";

$str = preg_replace_callback('=\[list\](.*)\[/list\]=Uis','liste', $str);

echo $str;


er matcht alles von bis (einschließlich) und ersetzt dadurch auch das zweite , sodass der zweite Eintrag nicht mehr gematcht werden kann.
Wie müsste ich einen Regex formulieren der von bis (ausschließlich) matcht?

Nase
2004-03-14, 14:21:22
Habe mich gestern auch einmal ein bisschen damit beschäftigt. Rausgekommen sind dabei zwei Funktionen:

<?php

// Bulletin Board Code


function bbcode ($code)
{

while($alter_code != $code)
{

$alter_code = $code;
$code = preg_replace_callback('{\[(\w+)((=)(.+))?\]((.|\n)*)\[/\1\]}U', "replace", $code);

}

return $code;

}


function replace ($code)
{

// $code[1]: der Tag wie b, i, u etc.
// $code[4]: erweiterter Tag, wie die Farbangabe bei color
// $code[5]: der von den Tags eingeschlossene Text

//alle Tags werden klein geschrieben
$code[1] = strtolower($code[1]);

switch ($code[1])
{
//Liste
case 'ul':
if(substr($code[5], 0, 3) == ' ')
{
$code[5] = ereg_replace('\[\*\]', '</li><li>', $code[5]);
$code[5] = substr($code[5], 5, strlen($code[5])) . '</li>';
$replace = '<' . $code[1] . '>' . $code[5] . '</' . $code[1] . '>';
}
else
$replace = '[' . $code[1] . ']' . $code[5] . '[' . $code[1] . ']';
break;

//fett
case 'b':
//kursiv
case 'i':
//unterstrichen
case 'u':
$replace = '<' . $code[1] . '>' . $code[5] . '</' . $code[1] . '>';
break;

//Schriftgroesse
case 'size':
$replace = '<span style="font-size: ' . $code[4] . 'px;">' . $code[5] . '</span>';
break;

//Schriftfarbe
case 'color':
if(ereg("^([[:xdigit:]]{3,6})$", $code[4]))
$replace = '<span style="color: #' . $code[4] . ';">' . $code[5] . '</span>';
else
$replace = '[' . $code[1] . $code[2] . ']' . $code[5] . '[' . $code[1] . $code[2] . ']';
break;

//IMG
case 'img':
$replace = '<img src="' . $code[5] . '" alt="" title="">';
break;

//URL
case 'url':
if($code[4])
$replace = '<a href="' . $code[4] . '" target="_new">' . $code[5] . '</a>';
else
$replace = '<a href="' . $code[5] . '" target="_new">' . $code[5] . '</a>';
break;

//eMail
case 'email':
if($code[4])
$replace = '<a href="mailto:' . $code[4] . '">' . $code[5] . '</a>';
else
$replace = '<a href="mailto:' . $code[5] . '">' . $code[5] . '</a>';
break;

//unbekannter Tag -> das eingegebene wird genauso wieder ausgegeben
default:
$replace = '[' . $code[1] . ']' . $code[5] . '[' . $code[1] . ']';
break;
}

return $replace;
}

?>

Oh man, das war ein hin und her gestern, bis das endlich alles lief.

MadMan2k
2004-03-14, 14:54:38
sieht auf den ersten Blick mächtig zu kompliziert aus...

für die Sachen außer der Lsite habe ich folgendes:


function changetext($str)
{

$str = htmlspecialchars($str); //HTML Code umwandeln
$str = preg_replace('=\[ b\](.*)\[/b\]=Uis','<span style="font-weight:bold;">\1</span>',$str); // Bold
$str = preg_replace('=\[ i\](.*)\[/i\]=Uis','<span style="font-style:italic;">\1</span>',$str); // Italic
$str = preg_replace('=\[ u\](.*)\[/u\]=Uis','<span style="text-decoration:underline;">\1</span>',$str); // Underline
$str = preg_replace('=\[img\](.*)\[/img\]=Uis','<img alt="externes Bild">\1</img>',$str);
$str = preg_replace('#\[url=(.*)\](.*)\[/url\]#Uis','<a href="\1">\2</a>',$str); // URL Tags
$str = preg_replace('#\[ color=(.*)\](.*)\[/color\]#Uis','<span style="color:\1;">\2</span>',$str); // Schriftfarbe
.
.
.

Nase
2004-03-14, 14:58:00
Im Grunde genommen genau dasselbe, nur alles in zwei Funktionen. Eine, die nach den Ausdrücken sucht, die andere, die mir die Tags ersetzt.

MadMan2k
2004-03-14, 15:49:32
Original geschrieben von Nase
Im Grunde genommen genau dasselbe, nur alles in zwei Funktionen. Eine, die nach den Ausdrücken sucht, die andere, die mir die Tags ersetzt.
stimmt, deine lösung ist sogar flexibler und leichter zuw arten, als meine...

aber kannst du mir vielleicht deinen RegEx etwas erklären?

[(w+)((=)(.+))?]((.|\n)*)[/1]

wieso musst du die '[]' nicht escapen?
(w+) = erstes Plattern matcht b, u usw...
was macht das '?' ?
((=)(.+)) = Plattern 2, müsste optional sein
(=) = Plattern 3, warum als Plattern?
(.+) = Plattern 4, matcht Eigenschaften
((.|\n)*) = Plattern 5, matcht Inhalt
(.|\n)* = Plattern6, warum nicht einfach '.*' ?

Nase
2004-03-14, 16:06:04
'{[(w+)((=)(.+))?]((.|\n)*)[/1]}U'

w+: es wird nach Werten zwischen [] gesucht.
((=)(.+))?: Nach einem Wert wie url kann ja ein optionaler Parameter eingefügt werden. Dieses sieht dann ja so aus [ url=http://www.undsoweiter.de]klick[ /url] (ohne die Leerzeichen nach den [). ? bedeutet ja einmal oder keinmal.
((.|\n)*): Aus dem Grund, da er sonst keine Zeilenumbrüche erkennt, wenn ich mich recht erinnere.
[/1]: Gibt mir den Wert vom Anfang wieder, allerdings ohne optionalen Parameter.

MadMan2k
2004-03-14, 16:45:29
thx.
hab jetzt auch meine Funktion entsprechend angepasst:


function liste($matches){
if(substr($matches[1], 0, 3) == ' '){
$replace = str_replace(' ', '</li><li>', substr($matches[1], 3, strlen($matches[1])));
return '<ul><li>'.$replace.'</li></ul>';
}
}

$str = "Hallo!\njetzt kommt ein Listentest:\n erster Eintrag zweiter Eintrag";

$str = preg_replace_callback('=\[list\](.*)\[/list\]=Uis','liste', $str);

echo $str;

MadMan2k
2004-03-14, 17:18:30
die while Schleife brauchst du übrigens nicht, da preg_replace_callback() greedy ist.

Nase
2004-03-14, 17:23:50
Leider funktioniert es dann aber nicht mehr richtig, weil sonst nur maximal ein Tag ausgetauscht werden kann.

MadMan2k
2004-03-14, 17:43:56
Original geschrieben von Nase
Leider funktioniert es dann aber nicht mehr richtig, weil sonst nur maximal ein Tag ausgetauscht werden kann.
hmm... bei mir hat es auch ohne funktioniert.
Sollte eigentlich auch, da preg_replace_callback() alle bb-codes matched und dann jedesmal die replace() aufruft...

jedenfalls habe ich es nun geschafft das ganze in eine Zeile zu quetschen :stolz:


$str = preg_replace_callback('=\[list\]\s*\[\*\](.*)\[/list\]=Uis',create_function('$matches','return "<ul>\n <li>" . str_replace(" ", "</li>\n <li>", $matches[1])."</li>\n</ul>\n";'), $str);

Nase
2004-03-14, 17:46:48
Bei mir ging es gerade nicht. Aber egal, hauptsache dein Problem ist gelöst :D.