Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

comment ecrire "tout sauf 0 a n fois" dans une regex?

7 réponses
Avatar
bruno
ma question se rsume a mon titre :

"comment ecrire "tout sauf <chaine> 0 a n fois" dans une regex?"

cad :


(copier coller de mon post sur developpez.net (
http://www.developpez.net/forums/viewtopic.php?t=406330&start=15 )
)


j'ai créé, pour une action de BTS, un moteur de template.
une de ses fonctionnalitées est de substituer un "bloque" par "autre
chose" (pas important ici)
pour substituer ce block, je doit tout d'abord l'extraire, pour ce
faire, je fait un preg_match() qui me retourne mon block, je fait mes
traitemeznts, puis je fait un str_replace() pour substituer le nouveuax

block.


mon pb se situe au niveau du preg_match :
cette fonction accepte trois parametre.
L'un d'eux est une expression reguliere.


cette expression est : (expliquée juste au dessus) :
"/<li>.*?\[ *testBDD *; *block *= *li *\].*<\/li>/sU"


mon pb :


au lieu de prendre le dernier <li> disponible, elle prend le
premier...cad :
Code:

<li> ne pas me prendre mias quand meme pris </li>
<li> je veut cette partie </li>
<li> ne pas me prendre et ca marche </li>


donnera :
Code:
<li> ne pas me prendre mias quand meme pris </li>
<li> je veut cette partie </li>


alors que je ne veut que :
Code:


<li> je veut cette partie </li>


meric de votre aide svp ))

7 réponses

Avatar
bruno
pourquoi cette question?
car je pense subir un bug de la part de PECR...
je suis en php 4.2.3, et je ne peut changer de version, si une ame
charitable voulait bien essayer de reproduire le pb pour moi...
le pb en question :

l'option ? apres un * est censé basculer en mode ungreedy...
ca marche pour le second, mais pas pour le premier...

voila deux notations differentes de la meme regex :
"/$blockDebut.*?[ *$indexBalise *; *block *= *$blockNom
*].*?$blockFin/s"
"/$blockDebut.*[ *$indexBalise *; *block *= *$blockNom
*].*$blockFin/sU"
le ? est remplacé par l'option /U qui rend totue l'expression non
gourmande...




il faudrait effectuer ce regex :
$motifBlock = "/$blockDebut.*?[ *$indexBalise *; *block *= *$blockNom
*].*?$blockFin/s";//ex : "<li>.[ *testBDD *; *block **([[:alnum:]]+)* *]].</li>"

preg_match($motifBlock, $template, $block);

avec comme valuer pour $template :

<ul><li>
[testBDD;block = li]
nom : [testBDD; nom] et id_outil =&nbsp;[testBDD; descriptif ] , enfin,
l'activitée est : [ testBDD ;libele]
</li>
</ul>

<ul>
<li class="Style1">
[testL2P ;block = li]
[testL2P; libele]
</li>
</ul>

je remercie d'avance l'ame charitable voulant bien efectuer ce test :))

normalement les deux blocs trouvés doivent etre "independant" mais das
la pretique, le debut du second se retrouve etre en fait le debut du
premeir (il pernd le <li> le plus loin possible autrement dit, il est
gourmand, malgres l'option activée...)
Avatar
bruno
je me susi trompé sur le motif a chercher :

"/<li>.*?[ *testL2P*; *block *= *li*].*?</li>/s"

est le bon, normalement , il ne faut pas que le block suivant soit
capturé avec :

<ul><li>
[testBDD;block = li]
nom : [testBDD; nom] et id_outil =&nbsp;[testBDD; descriptif ] , enfin,
l'activitée est : [ testBDD ;libele]
</li>
</ul>

desolé de mon erreure!
Avatar
Olivier Miakinen
Bonjour Bruno,

J'ai essayé de lire tes trois articles, mais c'est très touffu. Comme en
plus tu inverses souvent des lettres dans les mots, c'est d'autant plus
difficile à lire, et j'en viens à me demander s'il n'y a pas de telles
inversions dans les regexp (pas forcément dans ton programme, mais au
moins dans tes questions). Je vais essayer d'y voir plus clair malgré
tout, mais tu devras nous y aider...


"comment ecrire "tout sauf <chaine> 0 a n fois" dans une regex?"


Première question :
« (tout sauf chaîne) 0 à n fois »
ou
« tout sauf (chaîne 0 à n fois) »
?

[...]

pour substituer ce block, je doit tout d'abord l'extraire, pour ce
faire, je fait un preg_match() qui me retourne mon block, je fait mes
traitemeznts, puis je fait un str_replace() pour substituer le nouveuax


Avant d'aller plus loin, est-ce que preg_replace_callback() ne te
permettrait pas de faire tes traiztzmentzts en une seule fois, et
par là même de résoudre le problème ?
<http://fr.php.net/manual/fr/function.preg-replace-callback.php>

[...]

cette expression est : (expliquée juste au dessus) :
"/<li>.*?[ *testBDD *; *block *= *li *].*</li>/sU"


Une idée pour y voir plus clair serait de commencer par un cas plus
simple, dans lequel il n'y aurait pas toutes ces espaces optionnelles.
Cela allégerait ta regexp d'un certain nombre de " *" qu'il sera
toujours temps de rétablir par la suite.

au lieu de prendre le dernier <li> disponible, elle prend le
premier...cad :


Je lis bien : « au lieu de prendre le dernier <li> disponible, elle
prend le premier ».

Tu dis donc :
(1) elle prend le premier uniquement
(2) tu voudrais qu'elle prenne le dernier uniquement

Or :

Code:

<li> ne pas me prendre mias quand meme pris </li>
<li> je veut cette partie </li>
<li> ne pas me prendre et ca marche </li>

donnera :
Code:
<li> ne pas me prendre mias quand meme pris </li>
<li> je veut cette partie </li>


Ici, tu contredis le point (1) puisqu'elle ne prend pas le premier, mais
les deux premiers.

alors que je ne veut que :
Code:

<li> je veut cette partie </li>


Et là tu contredis le point (2) puisque tu demandes celui du milieu et
non le dernier.

meric de votre aide svp ))


Je vuxe bine t'airde mias il fuadrita y metter nu pue du tine...

--
Olivier Miakinen
Aidez Saburi à rester près de sa famille et poursuivre ses études
en France, signez la pétition :
<http://www.fcpe94.ouvaton.org/article_petition.php3?id_article=9>

Avatar
bruno
dsl, c'est vrai que je sait pas expliquer...

alors : preg_replace_callback() => je vait etudier cette fonction qui
m'a l'air tout a fait adaptée...(mais il me faudait d'autre param en
entrée, je vait voir tout ca...)

pour mon exemple :

<li> ne pas me prendre mias quand meme pris </li>
<li> je veut cette partie </li>
<li> ne pas me prendre et ca marche </li>


il faut minaginer cette regex :

"/<li>.*je veut cette partie.*</li>/sU"

le resultat renvoyé est alors :

array(1) {
[0]=&gt;
string(86) "<li> ne pas me prendre mias quand meme pris </li>
<li> je veut cette partie </li>"
}

je pense que tu est d'accord pour dire que seul la ligne du milieu
aurait du etre renvoyée?
je me demande s'il ne s'agi pas d'un bug... (je suis en php 4.2.3, pour
des raisons administratives...)
-----------------------------------------------------------
tu peut essayer de reproduire ce resultat :

$subject = " <li> ne pas me prendre mias quand meme pris </li>
<li> je veut cette partie </li>
<li> ne pas me prendre et ca marche </li> ";
preg_match("/<li>.*je veut cette partie.*</li>/sU", $subject ,
$result);
var_dump($result);

Avatar
Olivier Miakinen

<li> ne pas me prendre mias quand meme pris </li>
<li> je veut cette partie </li>
<li> ne pas me prendre et ca marche </li>


il faut minaginer cette regex :

"/<li>.*je veut cette partie.*</li>/sU"

le resultat renvoyé est alors :

array(1) {
[0]=&gt;
string(86) "<li> ne pas me prendre mias quand meme pris </li>
<li> je veut cette partie </li>"
}

je pense que tu est d'accord pour dire que seul la ligne du milieu
aurait du etre renvoyée?


Non, je suis d'accord avec le résultat renvoyé. Le « .* » récupère tout
ce qu'il peut (même les sauts de ligne grâce à l'option s). L'option U
permet de s'arrêter aussitôt qu'une correspondance est trouvée, ce qui
lui permet de ne pas prendre le contenu du 3e <li>, mais il n'a pas à
raccourcir la partie gauche.

Est-ce que entre <li> et </li> tu peux vraiment rencontrer n'importe
quel caractère, y compris les lettres "l" et "i", les chevrons "<" et
">", et même la barre "/" ?

Si ce n'est pas le cas, par exemple si tu ne peux pas rencontrer de "<"
entre le <li> et le début du truc à chercher, alors tu peux remplacer ta
regex par : "/<li>[^<]*je veut cette partie.*</li>/sU".

S'il n'y en a pas non plus à la fin, tu peux même te passer de l'option
U en écrivant : "/<li>[^<]*je veut cette partie[^<]*</li>/s".

Maintenant, si tu peux rencontrer des balises ouvrantes et fermantes,
par exemple "<li><em>lulli</em> je veux ça <em>lilo</em></li>", alors il
faudra peut-être chercher du côté des assertions. Mais peut-être pas.

Une autre solution, plus simple, serait de faire une simple recherche
sur "/<li>.*</li>/sU" et de traiter le contenu dans une seconde passe
(ou bien dans la même passe avec preg_replace_callback()).

--
Olivier Miakinen
Aidez Saburi à rester près de sa famille et poursuivre ses études
en France, signez la pétition :
<http://www.fcpe94.ouvaton.org/article_petition.php3?id_article=9>


Avatar
Olivier Miakinen

Maintenant, si tu peux rencontrer des balises ouvrantes et fermantes,
par exemple "<li><em>lulli</em> je veux ça <em>lilo</em></li>", alors il
faudra peut-être chercher du côté des assertions.


Si vraiment tu dois en arriver là, une solution consistera à remplacer
tes « .* » par des « ([^<]|<(?!li>))* » (non testé mais cela devrait
fonctionner).


Voici l'explication de cette formule barbare :

[^<]
= pas de chevron ouvrant "<"

<(?!li>)
= un chevron ouvrant "<" qui ne doit pas être suivi de "li>"

([^<]|<(?!li>))
= s'il y a un chevron ouvrant "<", il ne doit pas être suivi de "li>"
= pas de séquence "<li>"

([^<]|<(?!li>))*
= répéter sur tous les caractères

--
Olivier Miakinen
Aidez Saburi à rester près de sa famille et poursuivre ses études
en France, signez la pétition :
<http://www.fcpe94.ouvaton.org/article_petition.php3?id_article=9>

Avatar
bruno
Ca marche !!!!

"/<li>(.(?<!<li>))*[ *testBDD *; *block *= *li *].*</li>/sU"

telle est le type de motif qui fonctionne!
en versino simplifée :

"/<li>(.(?<!<li>))*.*</li>/sU"

MERCI A TOUS!!!!