Expression régulière regexp (not in)

Modérateur PHPfrance
Modérateur PHPfrance | 7636 Messages

29 janv. 2009, 20:06

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-|

ViPHP
ViPHP | 4039 Messages

29 janv. 2009, 21:25

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.
Mais qu'importe. (je suis ici - dernier petit projet)
Berze going social.

ViPHP
ViPHP | 5924 Messages

30 janv. 2009, 01:05

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.

Modérateur PHPfrance
Modérateur PHPfrance | 7636 Messages

30 janv. 2009, 11:10

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-)

/!\ Avant de poster se documenter et rechercher.
Qui ne sait pas rendre un service n'a pas le droit d'en demander.
MaBrute

ViPHP
ViPHP | 1380 Messages

30 janv. 2009, 12:58

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é.
ripat

Modérateur PHPfrance
Modérateur PHPfrance | 7636 Messages

30 janv. 2009, 14:14

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 :'(

/!\ Avant de poster se documenter et rechercher.
Qui ne sait pas rendre un service n'a pas le droit d'en demander.
MaBrute

ViPHP
ViPHP | 4039 Messages

03 févr. 2009, 12:32

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.
Mais qu'importe. (je suis ici - dernier petit projet)
Berze going social.