Comment capturer tout en excluant certaines chaînes? [RegEx] - PHP - Programmation
Marsh Posté le 03-10-2006 à 16:27:27
tu fais 2 regex ça ira plus vite et ça sera plus clair!!!
Code :
|
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!!!
|
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'.
Marsh Posté le 03-10-2006 à 16:55:30
ReplyMarsh 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...
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!
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. |
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?
Marsh Posté le 03-10-2006 à 17:52:24
Manquait un bout
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. |
Marsh Posté le 03-10-2006 à 20:03:49
ReplyMarsh 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 |
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é...
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 ?
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.
Marsh Posté le 03-10-2006 à 23:04:19
logique t as oublie les ^^^^^^^^^^ et $$$$$$$$$$$$$$$$$$$$$
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.
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.
|
Marsh Posté le 04-10-2006 à 00:45:14
Hmmmm, mais je me demande si vous le faites exprès !
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)
Marsh Posté le 04-10-2006 à 08:56:22
Ptin cette formulation du problème à la mord moi le noeud
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?
Marsh Posté le 04-10-2006 à 09:03:51
masklinn a écrit : Ptin cette formulation du problème à la mord moi le noeud |
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?
Marsh Posté le 04-10-2006 à 09:40:35
Bin non
tu dis ça et ensuite tu sors comme exemple:
Code :
|
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 :
|
Marsh Posté le 04-10-2006 à 09:52:20
anapajari a écrit : Bin non
en disant que ça marche pas! |
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 : |
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?
Marsh Posté le 04-10-2006 à 10:39:29
Code :
|
Marsh Posté le 04-10-2006 à 10:54:33
supermofo a écrit :
|
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?
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!
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.
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:
|
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...
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. |
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!
Marsh Posté le 05-10-2006 à 17:11:11
Au fait, ça veut dire quoi Gex? et Re aussi?
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.
Marsh Posté le 05-10-2006 à 17:35:32
En l'occurence c'est RegEx pour Regular Expressions, qu'on peut également écrire regex
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 |
Aussi "re" chez ces feignasses de devs Perl qui font chier avec le more than one way to do teh it
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 |
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 )
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): |
Yoyo@ a écrit : Ca marche! |
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
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).
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 |
En python aussi, parce que le module s'appelle "re"
Marsh Posté le 06-10-2006 à 09:45:01
anapajari a écrit : Bla Bla Bla |
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
Marsh Posté le 06-10-2006 à 10:05:29
ReplyMarsh Posté le 06-10-2006 à 10:08:50
supermofo a écrit : Deux fois t sur ? |
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...?
Marsh Posté le 07-10-2006 à 13:53:35
Oui n'hésite pas, le forum sert à ca
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 |
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 |
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)
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 ?
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