Page 1 sur 1

regex : Transformer HTML en BBCode

Posté : 17 févr. 2007, 21:30
par Arcanis
Salut!

Je suis en train de coder un éditeur wysiwyg pour un de mes projets et j'ai réussis. Maintenant, j'essaye de coder une fonction php pour qu'elle change le code html généré en bbcode. Mais j'ai quelques problème...

Ma fonction:
				$balises_to_replace = array(
						"\<b\>"                                               => "[b]",
						"\</b\>"                                              => "[/b]",
						"\<strong\>"                                          => "[b]",
						"\</strong\>"                                         => "[/b]",
						"\<font size=\"(.*)\"\>"                              => "[size=$1]",
						"\<font color=\"(.*)\"\>"                             => "[color=$1]",
						"\<img src=\"(.*)\"\>"                                => "[img]$1[/img]",
						"\<img src=\"(.*)\" width=\"(.*)\" height=\"(.*)\"\>" => "[img]$1[/img]",
						"\<a href=\"(.*)\"\>"                                 => "[url=$1]",
						"\</a\>"                                              => "[/url]",
						"\</a\>"                                              => "[/url]",
					);
					
				foreach($balises_to_replace as $balise => $bbcode) {
					$content = preg_replace("(".$balise.")i",$bbcode,$content);
				}
Texte de départ:

Code : Tout sélectionner

Ceci est un <font color="red">test pour<font size="4"> vér</font></font><font size="4">ifi<font color="green">er </font>que <font color="yellow">m</font></font><font color="yellow">on édit</font>eur marche correctement.<br>Test concluant?
Texte après traitement:

Code : Tout sélectionner

Ceci est un [color=red">test pour[size=4"> vér<font size="4">ifi<font color="green">er </font>que <font yellow]on="" édit="" color="#0e0000"></font>eur marche correctement.<br>Test concluant?
Vous ne verriez pas mon erreur dans les regex? Parce que j'ai beau les regarder 20x, je n'y trouve rien ... :cry:

Merci d'avance!

Posté : 17 févr. 2007, 21:41
par Jules Petibidon
hello,

tu utilise l'antislashe qui sert à échapper les guillemets comme délimiteur de masque... du coup l'analyse se prend les pieds dans le tapis et badaboum...

donc essaye en utilisant autre chose comme délimiteur de masque :)

Posté : 18 févr. 2007, 00:45
par Arcanis
Non, j'utilise des parenthèses comme délimiteur de masque (c'est dans la ligne suivante que se fait la transformation):
$content = preg_replace("(".$balise.")i",$bbcode,$content); 

Posté : 18 févr. 2007, 07:55
par Ripat
La gourmandise est un vilain défaut! Surtout pour les quantificateurs des regex.

Quand ton moteur de regex voit un motif comme (.*)\" il essaiera de capturer tout caractère jusqu'au dernier " qu'il rencontre.

Pour palier cette caractéristique des regex, tu peux rendre tes quantificateurs * non gourmands de différentes manières:
  • Avec le point d'interrogation derrière chaque quantificateur (.*?)\"
  • Avec l'option U derrière les délimiteurs
    preg_replace("(".$balise.")Ui",$bbcode,$content);
  • Enfin, plus élégant et efficace (plus rapide), remplacer le . qui mange tout par une classe de caractères négative: [^\"]*\" Dans ce cas, le moteur regex prendra tout caractère qui n'est pas un ". Ici, il n'est plus nécessaire de rendre le quantificateur non gourmand car le moteur regex s'arrêtera de lui-même dès qu'il rencontre un ".
Enfin, tu peux rendre tes motifs un peu plus lisibles en te débarrassant de tous les caractères d'échappement inutiles:
$balises_to_replace = array(
                        '<b>'                                               => '[b:89281ca8fb]',
                        '</b>'                                              => '[/b:89281ca8fb]',
                        '<strong>'                                          => '[b:89281ca8fb]',
                        '</strong>'                                         => '[/b:89281ca8fb]',
                        '<font size="([^"])*">'                             => '[size=$1]',
                        '<font color="([^"]*)">'                            => '[color=$1]',
                        '<img src="([^"]*)">'                               => '[img]$1[/img]',
                        '<img src="([^"]*)" width="[^"]*" height="[^"]*">'  => '[img]$1[/img]',
                        '<a href="([^"]*)">'                                => '[url=$1]',
                        '</a>'                                              => '[/url]',
                        '</a>'                                              => '[/url]',
                    );

Posté : 18 févr. 2007, 13:15
par Ryle
Je pense que Ripat a tout dit... ceci dit pour en revenir à ce que disait Jules, les parenthèses ne me semblent pas être un meilleur choix de délimiteur. Il s'agit de caractères réservés dans une expression régulière pour capturer les données. Le slash ( / ) ou le dièse ( # ) qui n'ont pas de signification dans une expreg me semblent beaucoup plus indiqués.

Sans quoi, c'est comme si sous MySQL je créeais une table nommée "select" avec un champ "from" : SELECT `from` FROM `select` ... ca fonctionne sous MySQL (fort heureusement les autres sgbd interdisent d'utiliser des mots clés réservés), mais y a rien de plus confus pour s'y retrouver :)

Posté : 18 févr. 2007, 16:20
par Arcanis
oki :-)
Par contre, il y a juste un petit truc que je n'ai pas compris: pourquoi ne pas échapper les caractères < et >? D'après l'explication de preg_quote, ils sont utilisés dans les regex, donc je pensais qu'il fallait les échapper.

Voilou. Merci encore!

Posté : 19 févr. 2007, 17:50
par Ripat
Par contre, il y a juste un petit truc que je n'ai pas compris: pourquoi ne pas échapper les caractères < et >? D'après l'explication de preg_quote, ils sont utilisés dans les regex, donc je pensais qu'il fallait les échapper.
Allez, comme je n'interviens plus très souvent sur ce forum, je me fends d'une longue explication. :wink:

< et > ne font pas partie des méta-caractères des pcre. Mais, effectivement, preg_quote les échappe pour une raison que j'ignore. Voici la liste officielle des méta-caractères:
\ general escape character with several uses
^ assert start of string (or line, in multiline mode)
$ assert end of string (or line, in multiline mode)
. match any character except newline (by default)
[ start character class definition
| start of alternative branch
( start subpattern
) end subpattern
? extends the meaning of (
also 0 or 1 quantifier
also quantifier minimizer
* 0 or more quantifier
+ 1 or more quantifier
also "possessive quantifier"
{ start min/max quantifier

Part of a pattern that is in square brackets is called a "character
class". In a character class the only metacharacters are:

\ general escape character
^ negate the class, but only if the first character
- indicates character range
[ POSIX character class (only if followed by POSIX
syntax)
] terminates the character class
http://www.pcre.org/pcre.txt
Quant aux délimiteurs, on peut y mettre tout caractère non alpha-numérique. Php y rajoute quelques bizarreries comme (xxxx) ou {xxxx} [xxxxx] <xxxxxx>. De toute les façons, c'est le problème de PHP, pas celui du moteur regex qui reçoit le motif d'un côté et les option de l'autre après que PHP ait parsé ses délimiteurs.

Les motifs suivants sont donc tout à fait légitimes à défaut d'être lisibles:
// liste (non exhaustive) de délimiteurs possibles
preg_match('&rocks&i', 'PHP rocks!')
preg_match('érockséi', 'PHP rocks!')
preg_match('§rocks§i', 'PHP rocks!')
preg_match('^rocks^i', 'PHP rocks!')
preg_match('èrocksèi', 'PHP rocks!')
preg_match('!rocks!i', 'PHP rocks!')
preg_match('*rocks*i', 'PHP rocks!')
preg_match('$rocks$i', 'PHP rocks!')
preg_match('+rocks+i', 'PHP rocks!')
preg_match('~rocks~i', 'PHP rocks!')
preg_match('=rocks=i', 'PHP rocks!')
preg_match('.rocks.i', 'PHP rocks!')
preg_match(';rocks;i', 'PHP rocks!')
preg_match('/rocks/i', 'PHP rocks!')
preg_match('?rocks?i', 'PHP rocks!')
preg_match('çrocksçi', 'PHP rocks!')
preg_match('-rocks-i', 'PHP rocks!')
preg_match('@rocks@i', 'PHP rocks!')
preg_match('°rocks°i', 'PHP rocks!')
preg_match('_rocks_i', 'PHP rocks!')
preg_match('ùrocksùi', 'PHP rocks!')
preg_match('µrocksµi', 'PHP rocks!')
preg_match('£rocks£i', 'PHP rocks!')
preg_match('|rocks|i', 'PHP rocks!')
preg_match('~rocks~i', 'PHP rocks!')

// bizarreries PHP
preg_match('(rocks)i', 'PHP rocks!')
preg_match('[rocks]i', 'PHP rocks!')
preg_match('{rocks}i', 'PHP rocks!')
preg_match('<rocks>i', 'PHP rocks!')
D'autres langages sont plus restrictifs sur l'emploi des délimiteurs: awk ,'accepte que le traditionnel /xxxx/, par contre sed est plus souple.

Code : Tout sélectionner

# awk echo 'PHP rocks!' | awk '/rocks/ {print}' # sed echo 'PHP rocks!' | sed 's/rocks/est génial/g' echo 'PHP rocks!' | sed 's%rocks%est génial%g' echo 'PHP rocks!' | sed 's{rocks{est génial{g' echo 'PHP rocks!' | sed 's%rocks%est génial%g'
Voilà, s'il y a ici un spécialiste PERL, qu'il parle maintenant ou se taise à jamais! :wink: