Page 1 sur 1

Expression régulière regexp (not in)

Posté : 29 janv. 2009, 20:06
par Truc
Bonjour, Bonsoir chèr(e) ... tout le monde

Après une très très longue journée de taff (depuis 4h... du mat) j'ai un peu de mal avec un pattern d'expression.

j'ai :

"<I>valeur1</I><b>valeur2</b><toto>valeur3</toto>ou tout autre < valeur ou ce truc > "

et je souhaiterai :

"<I>valeur1</I><b>valeur2</b>_toto_valeur3_/toto_ou tout autre _ valeur ou ce truc _ "

je veux remplacer les "<" et ">" par "_"
mais seulement s'ils sont suivis ou précédés de tags non connus.
Je connais un certain nombre de tags à ne pas remplacer (ici : I et B) et je veux remplacer tous les autres.

si l'expression vous saute aux yeux je prend :)

Il y a peut être autre chose mais je ne réfléchis plus des masses... peut être que la réponse me viendra pendant la nuit... ou pas... on ne sait jamais :)

Merci :)

Ps: à savoir que ça sera appliqué à du java par la suite 8-|

Posté : 29 janv. 2009, 21:25
par Berzemus
Oufti.. je ne suis pas peu fier..

Le plus facile, c'est quand même de remplacer les balises que tu veux garder par un ensemble exotique, et puis remplacer tous les <> par _ , avant de remettre en place les balises par après. Ca ira plus vite, à mon sens. Mais c'est pas cooooool.....


Sinon, pour le regex, c'est justement de faire en sorte qu'il ne s'appuie pas sur le nombre de caractères (très dur) et surtout qu'il n'exclue pas d'office les caractères des balises que tu veux garder (si on se base sur tout ce qui n'est pas I ou b, <bobo> ne validera pas..) ('achement trop dur).

Mais bon, si les balises que tu veux garder ne contiennent qu'une lettre, celle-ci devrait faire l'affaire:

Code : Tout sélectionner

(<)(/)?(?(2)([BIbi])?(?(3)q|[^BIbi][^>]*)|([BIbi/])?(?(4)q|[^BIbi/][^>]*))(>)
Le mieux, c'est que la balises genre "<o>" ben il les prend quand même (ça, c'est beau).

Pour filtrer, il suffit de remplacer le groupe 1 et 5 par _

Maintenant, je vais manger.. merci pour le casse-tête.

ps: le gros (très gros) inconvénient, c'est que si la balise commence par B, I, /B ou /I, ben ça passe travers.. genre </Boua> ... peut-être qu'il y a un moyen, mais en regex pur, c'est un peu du suicide quand même... le plus gros problème c'est la logique de regex (ou les perversité qu'on utilise pour aboutir à un système conditionnel). Pour une condition if (a) then (b) else (c), si le masque c valide a, a sera pris. Et pour le masque b, a est déjà décompté/capturé... Et ça devient très dur quand on cherche a capturer du contenu qu'on ne connait pas.

Posté : 30 janv. 2009, 01:05
par Sékiltoyai
Je te propose de diviser le pb en utilisant l'option e.
Tu captures tous les motifs <.*> et tu testes dans ton remplacement si c'est une balise que tu connais.

Posté : 30 janv. 2009, 11:10
par Truc
Je te propose de diviser le pb en utilisant l'option e.
Tu captures tous les motifs <.*> et tu testes dans ton remplacement si c'est une balise que tu connais.
m'oblige a faire plusieurs opérations, faisait parti d'une des solutions
merci
Le plus facile, c'est quand même de remplacer les balises que tu veux garder par un ensemble exotique, et puis remplacer tous les <> par _
j'y ai pensé avec le même avis... c'est pas coooool

mais je garde malgré tout l'expression en attendant tant pis pour les <bobo> et <igigi>
merci

:)

un nouveau casse tête... dans 1 an 8-)

Posté : 30 janv. 2009, 12:58
par Ripat
Je te propose de diviser le pb en utilisant l'option e.
Tu captures tous les motifs <.*> et tu testes dans ton remplacement si c'est une balise que tu connais.
C'est un bon conseil. En matière de regex, ne jamais chercher l'exercice de style en essayant de composer un seul motif passe-partout. Il faut penser au moteur regex qui doit faire autant d'aller-retours dans la chaîne à analyser qu'il y a de possibilités dans le motif. C'est ce backtracking qui tue les performances. Par contre je conseillerais plutôt preg_replace_callback pour des raisons de clarté de code (c'est personnel), mais surtout de performance (c'est factuel).
$txt  ='<I>valeur1</I><b>valeur2</b><toto>valeur3</toto>ou tout autre < valeur ou ce truc > ';

function cleanUpTags($captures){
    $authorizedTags = 'i|br|b|input|form';
   
    /* Si balise autorisée, aucune transformation. On retourne la balise telle quelle */
    if (preg_match('#(^</?'.$authorizedTags.'>$)#i', $captures[0])) {
        return $captures[0];        
    }
    
    /* Sinon on remplace les <> par _ */
    return preg_replace('#[<>]#', '_', $captures[0]);
}

/* On capture toutes les balises qu'on envoie dans la fonction callback pour traitement */
echo htmlentities(preg_replace_callback('#</?[^>]+>#', 'cleanUpTags', $txt)); 
Le gain en pref provient du fait que le moteur regex travaille en cascade sur des chaînes de plus en plus courtes.

preg_replace_call_back ---> preg_match --> preg_replace.

On peut, bien sûr, avantageusement remplacer le preg_match de la fonction par un strpos() approprié.

Posté : 30 janv. 2009, 14:14
par Truc
Merci Ripat... j'en retiens qu'il vaut mieux décomposer.
Me suis dis qu'il pouvait y avoir une super regex qui me ferait le tout en un seul replace :'(

Posté : 03 févr. 2009, 12:32
par Berzemus
Me suis dis qu'il pouvait y avoir une super regex qui me ferait le tout en un seul replace :'(
La solution de Ripat est de loin la meilleure. Pour le sport, ça peut être sympa de composer des expressions régulières cocasses, mais c'est très contreproductif.

Une réflexion que je me suis faite, juste au cas ou:

En regardant l'utilisation que fait perl de regex, par petits bouts, comme des masques. On voit bien que les expressions régulières ne sont pas du tout fait pour intégrer une structure conditionnelle, en plus, bonjour la maintenance.

C'est à coup de petites doses qu'il convient donc de l'utiliser, ce qui n'empêche pas de les optimiser (<[^>]+> plutôt que <.+>) et d'utiliser toutes ses possibilités.