Regex pour parser template php

Regex pour parser template php - PHP - Programmation

Marsh Posté le 18-09-2006 à 15:40:22    

Bonjour.
 
Le problème : je dois isoler au sein d'un fichier "template" des structures de type :
 
<balise> ... {instruction template} ... </balise>
 
sachant (et c'est là tout le problème) que les premiers trois petits points avant les accolades peuvent contenir de tout sauf l'expression <balise>. Le but est de boucler sur une instruction du type, par exemple :
 
<td><em>champ 1 : {afficher le contenu en bouclant sur td}</em></td>
 
en répétant tout le contenu situé entre les balises <td> et </td> (à la TinyButStrong, pour ceux qui connaissent...) mais en se limitant à l'encadrement <td> de premier niveau évidemment (on se penchera sur le multi niveau plus tard). Au passage, j'ai trouvé le code source de TBS un peu obscur sur ce point, avec des tonnes de méthodes / attributs persos dans la classe. Pourquoi pas des regex ?
 
Solution actuelle : regex de type #<([a-z]+)>.*?\{loop_\1([0-9]) \$([\.a-z]+)\}.*?</\1>#
(ne perdez pas de temps sur ce qu'il y a entre les accolades, c'est une instruction perso qui marche bien ;-).
Après les accolades, l'expression .*? fonctionne bien car elle s'arrête à la première balise </balise> rencontrée. En revanche, la même expression devant les accolades ne fonctionne pas de la même façon (malgré le point d'interrogation) car elle peut me renvoyer du texte contenant la balise <balise> AU CAS LIMITE OU LES ACCOLADES SONT ENTOUREES DE PLUSIEURS ENCADREMENTS DE <balise>. Il faut donc retraiter ce qu'elle me renvoie.
 
Solution recherchée : une regex qui puisse à la place du premier .*? rechercher "toute chaîne ne contenant pas l'expression <balise>". Si l'on prend une instruction du type [^abc], elle recherchera tout caractère sauf a, b ou c. Je cherche un moyen d'écrire le même type d'expression mais qui s'applique à une chaîne de caractère et non à simplement un caractère. Ecrire [^<balise>]* par exemple ne fonctionne pas car cela ne reconnaîtrait pas l'exemple du dessus, "champ 1" contenant un a.
 
Voilà, désolé pour ce post-fleuve. J'ai bouffé de la doc sur les regex, testé des tas de trus, mais n'ai pas trouvé d'instruction qui marche et qui permette directement cela.
 
Toute réponse / remarque / commentaire est bienvenu.
 
[ Mentral ]

Reply

Marsh Posté le 18-09-2006 à 15:40:22   

Reply

Marsh Posté le 18-09-2006 à 15:48:49    

à l'aveugle sans vraiment tester je dirais un truc dans le genre:

Code :
  1. #<([a-Z]+?)>(?<!$1).*?\{(.+?)\}.*?</$1>#


et vu que je sens que tu vas poser la question, le:

Code :
  1. (?<!$1)

est une "negative lookbehind assertion" ( assertion arrière négative in french I think)
 
Resultat non garanti :o
 
edit: huuummm ... tu as peut-être également besoin de l'assertion sur le .*? après tes accoldes


Message édité par anapajari le 18-09-2006 à 15:50:09
Reply

Marsh Posté le 18-09-2006 à 16:15:03    

Salut
 
Merci de ta réponse rapide, anapajari.
 
Toutefois, le lookbehind ne fonctionne pas ici car l'argument est dynamique et de longueur non fixe (c'est bien là tout le problème).  
 
 
 

Reply

Marsh Posté le 18-09-2006 à 17:21:49    

nemetral a écrit :

Toutefois, le lookbehind ne fonctionne pas ici car l'argument est dynamique et de longueur non fixe (c'est bien là tout le problème).


Ouais mais le truc c'est que tu fais balises par balises non? Ou alors j'ai pas compris ton exemple avec les td et les em :o
Dans celui-ci tu disais que tu devais remonter pour les "td" les pattern du type {...}. J'en ai déduis que cette balise était fixe ( ce qui n'est pas marqué dans la regex, je te l'accorde).
Du coup la longueur n'était pas censé être dynamique ;)
 
Si ce n'est pas le cas et que tu cherches pour toutes les balises je ne comprends pas bien comment tu vas faire, admettons sur le code suivant:

Code :
  1. <body>
  2. ...
  3. <table>
  4. <tr>
  5.    <td><em>champ 1 : {afficher le <b>contenu en bouclant sur td}</b></em></td>
  6. </tr>
  7. </table>
  8. ...
  9. </body>


Qu'est ce que tu veux matcher exactement???
 
Si on prend la regex suivante:

/<(.*?)>.*?\{(.*?)\}.*?<\/\1>/


Les ? la rendent non-greedy, tu vas donc récupérer le plus petit ensemble vérifiant tout ça, dans le cas ci-dessus cela sera forcément celui des "em".
Si tu vires les ?, tu la laisses greedy, et tu vas donc récupérer "body".
 
Ou alors tu cherches à récuperer:
- body  
- table
- tr
- td
Car les 4 contiennent bien une expression entre accolades, non précédée d'une balise identique.
 
Bref, n'explique un peu plus ;)


Message édité par anapajari le 18-09-2006 à 17:22:28
Reply

Marsh Posté le 19-09-2006 à 00:11:30    

nemetral a écrit :

Bonjour.
 
Le problème : je dois isoler au sein d'un fichier "template" des structures de type :
 
<balise> ... {instruction template} ... </balise>
 
sachant (et c'est là tout le problème) que les premiers trois petits points avant les accolades peuvent contenir de tout sauf l'expression <balise>. Le but est de boucler sur une instruction du type, par exemple :
 
<td><em>champ 1 : {afficher le contenu en bouclant sur td}</em></td>
 
en répétant tout le contenu situé entre les balises <td> et </td> (à la TinyButStrong, pour ceux qui connaissent...) mais en se limitant à l'encadrement <td> de premier niveau évidemment (on se penchera sur le multi niveau plus tard). Au passage, j'ai trouvé le code source de TBS un peu obscur sur ce point, avec des tonnes de méthodes / attributs persos dans la classe. Pourquoi pas des regex ?
 
Solution actuelle : regex de type #<([a-z]+)>.*?\{loop_\1([0-9]) \$([\.a-z]+)\}.*?</\1>#
(ne perdez pas de temps sur ce qu'il y a entre les accolades, c'est une instruction perso qui marche bien ;-).
Après les accolades, l'expression .*? fonctionne bien car elle s'arrête à la première balise </balise> rencontrée. En revanche, la même expression devant les accolades ne fonctionne pas de la même façon (malgré le point d'interrogation) car elle peut me renvoyer du texte contenant la balise <balise> AU CAS LIMITE OU LES ACCOLADES SONT ENTOUREES DE PLUSIEURS ENCADREMENTS DE <balise>. Il faut donc retraiter ce qu'elle me renvoie.
 
Solution recherchée : une regex qui puisse à la place du premier .*? rechercher "toute chaîne ne contenant pas l'expression <balise>". Si l'on prend une instruction du type [^abc], elle recherchera tout caractère sauf a, b ou c. Je cherche un moyen d'écrire le même type d'expression mais qui s'applique à une chaîne de caractère et non à simplement un caractère. Ecrire [^<balise>]* par exemple ne fonctionne pas car cela ne reconnaîtrait pas l'exemple du dessus, "champ 1" contenant un a.
 
Voilà, désolé pour ce post-fleuve. J'ai bouffé de la doc sur les regex, testé des tas de trus, mais n'ai pas trouvé d'instruction qui marche et qui permette directement cela.
 
Toute réponse / remarque / commentaire est bienvenu.
 
[ Mentral ]


 
C'est simple pourtant :
 
Dans le cas où tu as simplement:  
 

Code :
  1. //par deduction
  2. // pour <balise ...>{instruction template}</balise>
  3. $nivo0 = preg_match("@(<[^][>]+> )({[^][<]+})(</[a-z]+> )@", $str, $match)
  4. // recupere $1 = <balise class=... onfocus=''>  , $2 = le milieu
  5. //pour <balise ...>Mon texte qui sert a rien {mes templates}</balise>
  6. $nivo1 = preg_match("@(<[^][>]+> )(.+{[^][<]+})(</[a-z]+> )@", $str, $match)
  7. // pour des imbrications <balise><em> Mon texte qui sert a rien {template}</em></balise> avec 1 niveau de profondeur
  8. $nivo2 = $nivo1 = preg_match("@(<[^][>]+> )(<[^][>]+> )?(.+{[^][<]+})(</[a-z]+> )?(</[a-z]+> )@", $str, $match)


 
Apres je les connait pas tes instructions, donc le code si dessus est relativement imprécis (donc lourd) mais il marche a perfection.
 
L'amour des regex  :hello:
 
Edit : Les templates regex c périmé non ?


Message édité par supermofo le 19-09-2006 à 00:13:53
Reply

Marsh Posté le 19-09-2006 à 09:07:17    

S'quoi ce truc là :??:

<[^][>]+>


Soit tu as oublié de backslashé les ] internes mais je n'en comprendrais pas l'intêret
Soit tu as merdouillé sur la déclaration de "tout sauf un >", qui aurait du êre [^>]
Quoi qu'il en soit c'est une écriture lourde pour rien car cela revient à

<(.+?)>


[:spamafote]
 
En dehors de ces problèmes, ta dernière regex ne marche pas non plus si tu as du texte entre tes deux balises :o


Message édité par anapajari le 19-09-2006 à 09:08:55
Reply

Marsh Posté le 19-09-2006 à 13:12:14    

Ce truc la ca veut dire tout sauf > .  En gros si tu vx que ton texte ne contienne que du a-z et des espaces et rien d'autre.
 

Code :
  1. $sans_az = preg_match("@[^][ a-z]@si",$textebordelique,$match);
  2. //si le preg a trouve quelquechose alors la chaine contient autre chose //que a-z ou " "
  3. if($sans_az) echo "chaine invalide";
  4. else echo "chaine valide";


 
Pour <balise><em> Mon texte qui sert a rien {template}</em></balise>
la derniere regex retourne:
 

Code :
  1. //NOTA le preg_replace de HFR ajoute des espaces apres les ">" fo les virer
  2. $arr = preg_match("@(<[^][>]+> )(<[^][>]+> )?(.+{[^][<]+})(</[a-z]+> )?(</[a-z]+> )@", $str, $match) ;
  3. $1 = <balise...>
  4. $2 = <em..>
  5. $3 = Mon texte qui sert a rien {template}
  6. $4 = </em>
  7. $5 = </balise>


 
Ca marche tres bien. Il faut juste virer les espaces apres les >.
Pas artistique du tout mais c'est une solution parmi tant d'autres.
 
Sinon pour le [^][..] je l'utilise dans le filtrage d'url de la manière suivante

Code :
  1. //ca te vire tous les caracteres invalides pour url
  2. $str = preg_replace("@[^][a-z-]@si","-",$str);

Reply

Marsh Posté le 19-09-2006 à 13:19:35    

C'est juste que "normalement" ça s'écrit:

[^>]

et non

[^][>]


[:spamafote]
Et pour ta dernière regex je te parlais d'un truc dans le genre:

<balise>bla bla bla<em> Mon texte qui sert a rien {template}</em>pouet pouet</balise>


voir même

<balise>bla bla bla<em> Mon texte qui sert a rien {template}</em>pouet <em>pouet</em></balise>


:o


Message édité par anapajari le 19-09-2006 à 13:20:26
Reply

Marsh Posté le 19-09-2006 à 14:50:09    

@ supermofo : pourquoi les templates regex sont-ils périmés ?
 
Le poste initial n'était peut-être pas des plus clairs, j'en conviens. En gros, la regex que je recherche (si tant est qu'elle existe) serait capable d'identifier correctement chacun des exemples suivants :  
 

Code :
  1. # EXEMPLE 1
  2. <tr>
  3.     <td>
  4.        <em><a>{loop_td $user.name}</a></em>
  5.     </td>
  6. </tr>


 

Code :
  1. # EXEMPLE 2
  2. <tr>
  3.     <td>
  4.        <em><td><a>{loop_td $user.name}</a></td></em>
  5.     </td>
  6. </tr>


 

Code :
  1. # EXEMPLE 3
  2. <ul>
  3.     <li><li>{loop_li2 $user.id}</li></li>
  4. </ul>


 
et me retournerait respectivement :  
 

Code :
  1. <td>
  2.    <em><a>{loop_td $user.name}</a></em>
  3. </td>


 

Code :
  1. <td><a>{loop_td $user.name}</a></td>


 

Code :
  1. <li><li>{loop_li2 $user.id}</li></li>


 
NB : la dernière expression est un peu particulière : il s'agit d'englober deux niveaux de <li>.
 
Le problème est que le nombre de balises à encadrer ainsi peut changer.
 
En attendant je pense coder la recherche de la bonne expression moi-même ; je voulais faire appel à une regex pour des questions de performance sur une expression complexe, plutôt que de coder l'isolement de la portion de code correcte "à la main", à coups de while par exemple.

Reply

Marsh Posté le 19-09-2006 à 16:19:19    

Mais c'est l'éternel debat des templates. Il faut m'excuser je suis complètement chaos, je vais tenter de te repondre dans les 5 min qui me reste avant mon prochain café.
 
Php à la base fait TOUT ce dont tu as besoin, et tu n'as pas besoin de reinventer la roue.
 
Si tu me demandes mon avis je te dirais que je trouve que les templates ne servent à rien, que les include de page html sont mieux mais que le gain de performance est nul par rapport un a gros fichier php et html.
Je dirais aussi qu'il faut que tous les developpeurs du monde retourne au HTML 1.0 ...
 
Après chacun fait comme il vt : je t'invite donc a lire les posts suivants ou tu trouves une belle collection de posts:
- http://forum.hardware.fr/hardwaref [...] 5724-1.htm
- http://forum.hardware.fr/hardwaref [...] 8003-1.htm
 
Dans tous les cas le seul moyen de tester si ta solution est viable est de la comparer à la plus rapide dans le domaine (si tu la trouves bien sur)
 
Edit: Pour ton moteur si il y a un nombre de balise variable. Tu cherches le maximum possible et tu les mets dasn ta regex en optionnel ou bien tu passes le nombre de balise à une fonction qui feras le reste ???  :pt1cable:


Message édité par supermofo le 19-09-2006 à 16:25:07
Reply

Marsh Posté le 19-09-2006 à 16:19:19   

Reply

Marsh Posté le 19-09-2006 à 16:27:05    

nemetral a écrit :

je voulais faire appel à une regex pour des questions de performance sur une expression complexe, plutôt que de coder l'isolement de la portion de code correcte "à la main", à coups de while par exemple.


faudrait tester, parce que c'est pas du tout certain que la regex soit plus performante, ça dépend des cas.

Reply

Marsh Posté le 19-09-2006 à 16:35:29    

Ok merci à tous.
On va pas épiloguer là-dessus, je vais coder le truc à la main.
 
Concernant l'utilité du moteur de template, sois rassuré supermofo, je ne la voyais pas moi-même il y a de ça une semaine. Mais soudain ça m'a pris, pour le "défi" que ça représente plus que pour sa valeur ajoutée. D'autant plus que le framework que je développe fonctionne avec des "templates" pur php.

Reply

Marsh Posté le 19-09-2006 à 16:50:10    

nemetral a écrit :

Ok merci à tous.
On va pas épiloguer là-dessus, je vais coder le truc à la main.
 
Concernant l'utilité du moteur de template, sois rassuré supermofo, je ne la voyais pas moi-même il y a de ça une semaine. Mais soudain ça m'a pris, pour le "défi" que ça représente plus que pour sa valeur ajoutée. D'autant plus que le framework que je développe fonctionne avec des "templates" pur php.


 
Ouais bon courage, rien de tel que du bon code perso  :wahoo:

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed