[RegEx]Comment capturer tout en excluant certaines chaînes?

Comment capturer tout en excluant certaines chaînes? [RegEx] - PHP - Programmation

Marsh Posté le 03-10-2006 à 15:43:09    

Salut,
 
Existerait il un pattern qui me permettrait de capturer toutes les séquence de caractères commençant par "abc", finissant par "xyz" et ne contenant ni "lm" ni "pq"?.
 
En fait, je ne sais pas, a priori, comment faire pour exclure des séquences entières de caractères? (s'il s'agissait simplement d'exclure des caractères, j'utiliserais des classes de caractères avec un petit '^'.)
 
PS: J'utilise les ReGex au sens Perl.


Message édité par Yoyo@ le 05-10-2006 à 17:18:24
Reply

Marsh Posté le 03-10-2006 à 15:43:09   

Reply

Marsh Posté le 03-10-2006 à 16:27:27    

tu fais 2 regex ça ira plus vite et ça sera plus clair!!!

Code :
  1. if ( $a =~ /^abc.*xyz$/  && $a!~ /(ln|pq)/ ){
  2. ...
  3. }

Reply

Marsh Posté le 03-10-2006 à 16:35:02    

anapajari a écrit :

tu fais 2 regex ça ira plus vite et ça sera plus clair!!!

Code :
  1. if ( $a =~ /^abc.*xyz$/  && $a!~ /(ln|pq)/ ){
  2. ...
  3. }



 
Merci pour ta réponse...
 
Mais finalement, faire 2 regex, ça me pousse à en faire...3 (et encore...) car le but est de faire un preg_replace du genre:
 

$str = preg_replace('/abc....xyz/', '', $str);


 
Cad que je veux virer la chaîne entière si jamais je ne trouve ni mon ln ni mon pq.
 
(par contre, en fait, je n'ai pas besoin que abc soit le début de la chaîne de test et que xyz soit la fin, donc, j'ai enlevé le ^ et le $)
 
En gros, mon but est d'éliminer toute chaîne de caractère commençant par abc, finissant par xyz et ne contenant ni 'ln' ni 'pq'.

Reply

Marsh Posté le 03-10-2006 à 16:55:30    

ah tiens encore le fameux =~ ca veut dire quoi ?

Reply

Marsh Posté le 03-10-2006 à 16:58:23    

mIRROR a écrit :

ah tiens encore le fameux =~ ca veut dire quoi ?


 
Bah c'est l'équivalent du preg_match en Perl...

Reply

Marsh Posté le 03-10-2006 à 17:05:31    

si t'as chaine comprise entre abc et xyz c'est possible en utilisant une assertion négative.  
Dans le cas contraire, je pense pas!

Reply

Marsh Posté le 03-10-2006 à 17:11:02    

anapajari a écrit :

si t'as chaine comprise entre abc et xyz c'est possible en utilisant une assertion négative.  
Dans le cas contraire, je pense pas!


 
Ouh là, je ne suis pas certain d'avoir compris ta phrase.
 
En gros,
 
si on cosnidère l'expression
$str = preg_replace('/abc[^lp]*xyz/', '', $str)
 
Elle permet d'effacer toute chaîne commençant par abc, finissant par xyz et ne contenant ni l ni p.
 
Je voudrais faire la même chose, mais au lieu de faire porter ma condition sur "ne contenant pas tel ou tel caractère", je voudrais que ma condition soit plutôt "ne contenant pas telle ou telle chaîne".
 
Suis-je clair?

Reply

Marsh Posté le 03-10-2006 à 17:52:24    

Manquait un bout :o

anapajari a écrit :

si t'as chaine comprise est de longueur fixe entre abc et xyz c'est possible en utilisant une assertion négative.  
Dans le cas contraire, je pense pas!


Message édité par anapajari le 03-10-2006 à 17:52:43
Reply

Marsh Posté le 03-10-2006 à 17:56:46    

/abc[^(lp|pq)]*xyz/

Reply

Marsh Posté le 03-10-2006 à 20:03:49    

anapajari a écrit :

/abc[^(lp|pq)]*xyz/



a ben finalement j aurais du poster ma reponse alors :D

Reply

Marsh Posté le 03-10-2006 à 20:03:49   

Reply

Marsh Posté le 03-10-2006 à 21:52:42    

L'idée est intéressante, mais...ça n'a pas l'air de marcher.
 
En effet, le code suivant:
 

<?php  
 $str = 'abcdefghiklamnopaqrstuvwxyz';
 $str = preg_replace('/abc[^(lm|pq)]*xyz/', '', $str);
 print $str;
?>


 
devrait en théorie me retourner une chaîne vide, mais ce n'est pas le cas...Il me retourne la chaîne originale dans son intégralité...

Reply

Marsh Posté le 03-10-2006 à 22:15:40    

ha non tu as interdit lm et pq mais dans ta chaine t as lam et paq donc logique que ca passe non ?

Reply

Marsh Posté le 03-10-2006 à 22:37:19    

Bah nan, ce que je demande, c'est que toute chaîne commençant par abc et terminant par xyz, et ne contenant ni lm ni pq, soit remplacée par la chaîne vide.
 
Et donc, ici, ça ne marche pas, même si d'un point de vue logique, en lisant la regex, on aurait l'impression que ça pourrait marcher (sous réserve que la syntaxe soit correcte)
 
Je répète encore une fois la problématique: je souhaite effacer toute chaîne commençant par abc, finissant par xyz et ne contenant ni lm ni pq.

Reply

Marsh Posté le 03-10-2006 à 23:04:19    

logique t as oublie les ^^^^^^^^^^ et $$$$$$$$$$$$$$$$$$$$$

Reply

Marsh Posté le 03-10-2006 à 23:21:19    

Mais nan, je ne demande pas que le abc et le xyz soit systématiquement en début et fin de chaîne (je me suis sans doute mal exprimé), mais simplement, de détecter toutes les chaînes contenant un abc, suivi d'une suite quelconque de caractères ne contenant ni lm ni pq, et enfin, content un xyz, et de remplacer tout ce beau monde par une chaîne vide.

Reply

Marsh Posté le 04-10-2006 à 00:14:32    

Yoyo@ a écrit :

L'idée est intéressante, mais...ça n'a pas l'air de marcher.
 
En effet, le code suivant:
 

Citation :

<?php  
 $str = 'abcdefghiklamnopaqrstuvwxyz';
 $str = preg_replace('/abc[^(lm|pq)]*xyz/', '', $str);
 print $str;
?>


 
devrait en théorie me retourner une chaîne vide,  
non ca retourne la chaine :fou:
 
mais ce n'est pas le cas...Il me retourne la chaîne originale dans son intégralité...
 
normal :o


Message édité par mIRROR le 04-10-2006 à 00:14:58
Reply

Marsh Posté le 04-10-2006 à 00:45:14    

Hmmmm, mais je me demande si vous le faites exprès :D!
 
Bon, alors traduction de la Regex suivante:  preg_replace('/abc[^(lm|pq)]*xyz/', '', $str);
On cherche les chaînes commençant par abc, se terminant par xyz, et contenant tout ce que l'on veut, hormis lm ou pq. Et si chaîne trouvée, alors la remplacer par la chaîne vide.
 
Bref, c'est bien ce que je veux...mais ça ne marche pas.
 
Et pour info, vu que vous m'avez mis le doute, j'ai tenté la mêem chose, mais en mettant un 'abcdefghiklmnopqrstuvwxyz' en entrée, et là encore, ça me retourne ma chaîne originale...
 
Bref, toujours pas de réponse pour le moment... (même si le pattern donné plus haut me plaisait bien)
 

Reply

Marsh Posté le 04-10-2006 à 08:56:22    

Ptin cette formulation du problème à la mord moi le noeud [:pingouino]
 
Pour simplifier, tu ne veux garder que les chaînes commençant par abc terminant par xyz et contenant lm ou pq, et tu veux virer tout le reste, rite?

Reply

Marsh Posté le 04-10-2006 à 09:03:51    

masklinn a écrit :

Ptin cette formulation du problème à la mord moi le noeud [:pingouino]
 
Pour simplifier, tu ne veux garder que les chaînes commençant par abc terminant par xyz et contenant lm ou pq, et tu veux virer tout le reste, rite?


 
Tu concèderas que dire :
tu ne veux garder que les chaînes commençant par abc terminant par xyz et contenant lm ou pq, et tu veux virer tout le reste,
 
et  
 
ce que je demande, c'est que toute chaîne commençant par abc et terminant par xyz, et ne contenant ni lm ni pq, soit remplacée par la chaîne vide.
 
sont équivalents? (en fait, pas exactement, car dans ma formulation, je dis ce que je veux virer, donc, finalement, s'il n'y a rien à virer, ma chaîne reste la même)
 
Un exemple:
 
Je veux que cette chaîne:
 
cdhqsjhuhezhkjhlabckjffjlskxyzfslkfjfjkjabckjfsdlkjfqkflmdhfkjqhskfkdfxyzfsdf
soit transformée en:
cdhqsjhuhezhkjhlfslkfjfjkjabckjfsdlkjfqkflmdhfkjqhskfkdfxyzfsdf
 
Est ce clair?


Message édité par Yoyo@ le 04-10-2006 à 09:04:41
Reply

Marsh Posté le 04-10-2006 à 09:40:35    

Bin non :o
tu dis ça et ensuite tu sors comme exemple:

Code :
  1. $str = 'abcdefghiklamnopaqrstuvwxyz';

en disant que ça marche pas!
Ce que tu veux est plus compliqué! Tu veux virer toutes les chaines commençant par abc, terminées par xyz et ne contenant pas soit l et m ( pas forcément adjacent) soit p et q ( pas forcément adjacent).
Est-ce bien ça?
 
Masklin> Elle est ou ta quote avec les regex du mec de emacs? Elle irait parfaitement ici.
edit: retrouvée:

masklinn a écrit :

Citation :

'Some people, when confronted with a problem, think "I know, I'll use regular expressions".  Now they have two problems.'  -- Jamie Zawinski, comp.lang.emacs


Message cité 1 fois
Message édité par anapajari le 04-10-2006 à 09:41:39
Reply

Marsh Posté le 04-10-2006 à 09:52:20    

anapajari a écrit :

Bin non :o
tu dis ça et ensuite tu sors comme exemple:

Code :
  1. $str = 'abcdefghiklamnopaqrstuvwxyz';

en disant que ça marche pas!
Ce que tu veux est plus compliqué! Tu veux virer toutes les chaines commençant par abc, terminées par xyz et ne contenant pas soit l et m ( pas forcément adjacent) soit p et q ( pas forcément adjacent).
Est-ce bien ça?


Presque...
Je veux virer toutes les (sous)chaînes inclues dans une chaîne principale, commençant par abc, finissant par xyz, et ne contenant pas lm et pq (adjacents donc)
 
....abc...lm....pq....xyz.... je garde la sous chaîne
 
....abc.......pq....xyz.... je vire la sous chaîne
 
....abc..........xyz.... je vire la sous chaîne
 
....abc...l...m....pq....xyz.... je vire la sous chaîne
 
Par contre ya une précision que je n'ai pas apportée: peu importe l'ordre relatif entre lm et pq. Je veux juste faire un test simple me disant s'il y a lm et pq, alors je garde, sinon, je vire.
 
Je comprends bien qu'entre les "et", les "ou", les "ni", les "soit", il devient facile de s'embrouiller, mais je pense que je suis cohérent (enfin je crois??) depuis le début.
 

anapajari a écrit :


Masklin> Elle est ou ta quote avec les regex du mec de emacs? Elle irait parfaitement ici.
edit: retrouvée:


 
Lol!
Mais rassure moi, mon problème me paraiît adapté aux regex?
 
Sinon, pour info, jai trouvé une autre manière de le faire, c'est de faire un split de ma chaîne principale, en splittant sur /abc.*xyz/U et ensuite, en testant mes délimiteurs (ça me donne un tableau) et en virant les délimiteurs ne satisfaisant pas ma condition et en réassemblant le reste. Mais j'ai peur que sur une grosse chaîne, ça me bouffe un max de mémoire?

Reply

Marsh Posté le 04-10-2006 à 10:39:29    

Code :
  1. preg_match("@abc(.*)xyz@si",$chaine ,$res);
  2. if ( (stristr($res[1], 'lm') && stristr($res[1], 'pq')) === FALSE ){
  3. $res[1]='';
  4. }
  5. $souchaine = $res[1] ;

Message cité 1 fois
Message édité par supermofo le 04-10-2006 à 10:42:09
Reply

Marsh Posté le 04-10-2006 à 10:54:33    

supermofo a écrit :

Code :
  1. preg_match("@abc(.*)xyz@si",$chaine ,$res);
  2. if ( (stristr($res[1], 'lm') && stristr($res[1], 'pq')) === FALSE ){
  3. $res[1]='';
  4. }
  5. $souchaine = $res[1] ;



 
oki!
 
Donc, déjà, ça semble répondre à ma question initiale: il n'y a pas de pattern qui puisse me permettre de faire ça directement, que j'aurais directement intégré à pref_replace.
 
Ensuite, dans ton exemple, comment faire pour réintégrer $souchaine  à la chaîne principale?

Reply

Marsh Posté le 04-10-2006 à 11:19:09    

Si l'ordre d'apparition de lm et pq est toujours le même tu peux t'en sortir en faisant:

/abc(?:(?!(lm)).)+(?:(?!(pq)).)+xyz/


Mais par contre si pq peut se trouver avant lm ça marchera pas!

Reply

Marsh Posté le 05-10-2006 à 13:38:19    

Hint: vu comme les gens se prennent la tête sur le problème, je rappelle à tout le monde que vouloir le résoudre avec une expression régulière n'est pas forcément la meilleure idée, et que deux trois lignes de code peuvent parfaitement simplifier le travail. [:dao]

Reply

Marsh Posté le 05-10-2006 à 16:59:09    

anapajari a écrit :

Si l'ordre d'apparition de lm et pq est toujours le même tu peux t'en sortir en faisant:

/abc(?:(?!(lm)).)+(?:(?!(pq)).)+xyz/


Mais par contre si pq peut se trouver avant lm ça marchera pas!


 
Ca marche!
 
Bien joué! Je pense que tu as bien dû te tordre le cerveau pour trouver ça. (en fait, tout vient de l'astuce du ?! qui finalement a un effet comparable au ^, mais sur plusieurs caractères)
 
N'empêche, j'aimerais trouver plus simple, car ça rend le code vraiment peu lisible et difficile à comprendre...

Reply

Marsh Posté le 05-10-2006 à 17:03:03    

Chaos Intestinal a écrit :

Hint: vu comme les gens se prennent la tête sur le problème, je rappelle à tout le monde que vouloir le résoudre avec une expression régulière n'est pas forcément la meilleure idée, et que deux trois lignes de code peuvent parfaitement simplifier le travail. [:dao]


 
Ecoute, je veux bien, et je suis d'accord avec toi.
 
J'aimerais pouvoir implémenter un algorithme du genre (qui nécessiterait donc plusieurs regex):
 
Tant que je trouve un pattern commençant par abc et finissant par xyz
  Je regarde si il y a lm puis pq dans ma chaîne.
  Si ce n'est pas le cas, je remplace ma chaîne capturée par abcxyz dans la chaîne principale
 
Ce genre d'algorithme fonctionnerait bien, mais mon seul problème serait de pouvoir faire le remplacement que je cite à la fin: ça nécessite de retrouver ma sous chaîne dans la chaîne principale, pour la remplacer par abcxyz, et ça, je trouve que ça a un côté un peu inutile, vu que ma chaîne a déjà été trouvée une première fois.
 
Edit: Bon, je viens de voir que preg_match sous PHP dispose d'un flag PREG_OFFSET_CAPTURE qui permet de savoir où exactement le match a été fait. Donc, grâce à ce flag, je peux facilement réintégrer ma chaîne dans ma chaîne de départ, puisque je peux savoir à quel endroit ma sous chaîne a été extraite. Cool!

Message cité 1 fois
Message édité par Yoyo@ le 05-10-2006 à 17:07:10
Reply

Marsh Posté le 05-10-2006 à 17:11:11    

Au fait, ça veut dire quoi Gex? et Re aussi?


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 05-10-2006 à 17:18:00    

masklinn a écrit :

Au fait, ça veut dire quoi Gex? et Re aussi?


 
Je ne sais pas...je ne connais que les ExpReg, j'ai donc dû faire des fautes de frappe. :D

Reply

Marsh Posté le 05-10-2006 à 17:35:32    

En l'occurence c'est RegEx pour Regular Expressions, qu'on peut également écrire regex [:dawa]


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 05-10-2006 à 17:38:16    

masklinn a écrit :

En l'occurence c'est RegEx pour Regular Expressions, qu'on peut également écrire regex [:dawa]


 
Aussi "re" chez ces feignasses de devs Perl qui font chier avec le more than one way to do teh it [:dawak]

Reply

Marsh Posté le 05-10-2006 à 18:19:20    

masklinn a écrit :

En l'occurence c'est RegEx pour Regular Expressions, qu'on peut également écrire regex [:dawa]


 
Bah vi, je sais bien, d'où mon ':D' dans ma réponse.
 
Et effectivement, sous Perl, on parle de re. (les connaisseurs connaissent bien perlre ;) )

Reply

Marsh Posté le 05-10-2006 à 18:32:31    

Yoyo@ a écrit :

J'aimerais pouvoir implémenter un algorithme du genre (qui nécessiterait donc plusieurs regex):
Tant que je trouve un pattern commençant par abc et finissant par xyz
  Je regarde si il y a lm puis pq dans ma chaîne.
  Si ce n'est pas le cas, je remplace ma chaîne capturée par abcxyz dans la chaîne principale



Donc non il n'y a pas besoin de plusieurs regexs ...

Yoyo@ a écrit :

Bien joué! Je pense que tu as bien dû te tordre le cerveau pour trouver ça. (en fait, tout vient de l'astuce du ?! qui finalement a un effet comparable au ^, mais sur plusieurs caractères)


pour être honnête je me suis sortout tordu le crane a comprendre ce que tu voulais [:dawa]

Yoyo@ a écrit :

N'empêche, j'aimerais trouver plus simple, car ça rend le code vraiment peu lisible et difficile à comprendre...


Les regex un brin compliqué c'est toujours illisible et difficile à comprendre, sauf si tu as le droit au verbose regular expression ( genre en python ou en perl).

Message cité 1 fois
Message édité par anapajari le 05-10-2006 à 18:33:31
Reply

Marsh Posté le 05-10-2006 à 18:45:22    

Chaos Intestinal a écrit :

Aussi "re" chez ces feignasses de devs Perl qui font chier avec le more than one way to do teh it [:dawak]


En python aussi, parce que le module s'appelle "re" [:dawa]


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 06-10-2006 à 09:45:01    


 
Bon, me revoici de retour après quelques tests.
 
J'ai d'une part essayé avec ton pattern "/abc(?:(?!(lm)).)+(?:(?!(pq)).)+xyz/". Il marche très bien. Et finalement, je bloquais un peu dessus, car je ne connaissais pas les asertions du genre ?!, mais il n'est pas si compliqué que ça en définitive.
 
J'ai également essayé avec ma méthode itérative, cad que je fais un preg_match_all pour avoir toutes les strings commençant par abc et finissant par xyz, puis sur chacune d'entre elles, je fais un test pour m'assurer que ni lm ni pq n sont incluses dans la sous chaîne, et si c'est le cas, alors, dans ma chaîne principale, je remplace ma sous chaîne par une chaîne vide. Je peux le faire facilement grâce au flag PREG_OFFSET_CAPTURE.
 
Le résultat des courses, c'est que la méthode utilisant le patern uniqu et l'assertion ?! est plus de deux fois plus rapide que la méthode itérative donnée ci dessus.
 
Voilà!
 
Merci :hello:

Reply

Marsh Posté le 06-10-2006 à 10:05:29    

Deux fois t sur ?
 
Franchement j'en doute fort ...

Reply

Marsh Posté le 06-10-2006 à 10:08:50    

supermofo a écrit :

Deux fois t sur ?
 
Franchement j'en doute fort ...


 
Bah je ne sais pas si ma façon de tester est optimale, mais si tu veux, je peux te mettre mon code pour que tu me dises ce que tu en penses...?

Reply

Marsh Posté le 07-10-2006 à 13:53:35    

Oui n'hésite pas, le forum sert à ca  :sleep:


Message édité par supermofo le 07-10-2006 à 13:54:08
Reply

Marsh Posté le 10-10-2006 à 14:33:01    

oki, alors voici mes scripts de test.
 
J'ai décidé de simplifier un tout petit peu le problème, pour rendre le truc un peu plus lisible.
 
Le nouvel énoncé est le suivant:
 
Dans ma chaîne de test, je remplace toute séquence de caractères commençant par [/td] et finissant par [td] et ne contenant pas [tr], par [/td][td].
(en gros, je vire tout ce qu'il y a entre la fin d'une cellule et le début d'une nouvelle - c'est un exemple)
 
Voici la première version, qui utilise le pattern suggéré par anapajari:
 

<?php  
list($usec, $sec) = explode(" ", microtime());
$start = (float)$usec + (float)$sec;
 
for ($i=0;$i<100000;$i++){
 $str='Hello [table][tr][td][/td][td][/td][/tr][tr][td][/td]ffd
 sfd[td]ffdsfd[/td][/tr][/table]You';
 $str = preg_replace('/\[\/td\](?:(?!([tr])).)+\[td\]/sU', '[/td][td]', $str);
}
 
list($usec, $sec) = explode(" ", microtime());
$now = (float)$usec + (float)$sec;
print "Time Taken for " . $i . " iterations: " . ($now - $start) . " seconds";
?>


 
Pour 100k itérations, mon script prend 1.15s. (machine = Athlon XP @ 2GHz et 1Go de RAM)
 
Voici maintenant une version itérative, avec un pattern plus simple, accompagné d'un test, comme je l'explique plus haut:
 

<?php
list($usec, $sec) = explode(" ", microtime());
$start = (float)$usec + (float)$sec;
 
for ($i=0;$i<100000;$i++){
 $str = 'Hello [table][tr][td][/td][td][/td][/tr][tr][td][/td]ffd
sfd[td]ffdsfd[/td][/tr][/table]You';
 preg_match_all('/\[\/td\](.*)\[td\]/sU', $str, $matches, PREG_OFFSET_CAPTURE);
 for ($index=count($matches[1])-1; $index>=0; $index--){
  $tmp = $matches[1][$index][0];
  if(preg_match('/\[tr\]/', $tmp)){
   $str=substr_replace($str, '', $tmp, strlen($tmp));
  }
 }
}
 
list($usec, $sec) = explode(" ", microtime());
$now = (float)$usec + (float)$sec;
print "Time Taken for " . $i . " iterations: " . ($now - $start) . " seconds";  
?>


 
Je ne sais pas si cette version est optimale (autre solution?), mais toujours est il qu'elle prend sur la même machine 2.84s.
 
Maintenant, je ne sais pas non plus si le fait d'utiliser la même chaîne de caractère et un unique pattern (solution 1) n'a pas tendnace à optimiser les choses...
 
TOut commentaire est le bienvenu (pour le fun, bien entendu, vu que de toute manière, j'ai opté pour la version 1)
 

Reply

Marsh Posté le 10-10-2006 à 14:43:04    

La complexité du test reflète-t-elle la complexité "pire cas" des données que tu vas traiter ?

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

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