Regex contenue dans une chaine de caractère

Regex contenue dans une chaine de caractère - Perl - Programmation

Marsh Posté le 23-07-2004 à 10:35:40    

Salut,
 
je suis face à un petit pb dont je ne trouve pas de solution sur le net.
Avant tout, voici le code que je teste :
 

Code :
  1. my $chaine="coucou, ca va ?";
  2. my $regex="s/a/o/g";
  3. $chaine=~$regex;
  4. print "chaine : $chaine\n";


 
En gros j'essaie d'appliquer une regex sur une chaine, sachant que la définition complète de la regex est elle aussi dans une chaine.
Et ben ca, ca marche pas, la regex n'est pas appliquée.
 
J'ai essayé la regex "s/a/o/go" sans succes. Ha aussi, j'aimerais éviter d'avoir a analyser ma regex avec ... une regex pour en extraire les champs existants, car cette regex initiale doit etre appliquée un tres grand nombre de fois et j'ai peur que ca ralentisse tout !


Message édité par oliv5 le 23-07-2004 à 10:39:07
Reply

Marsh Posté le 23-07-2004 à 10:35:40   

Reply

Marsh Posté le 23-07-2004 à 10:38:43    

heu, une regexp c tjs une chaine de caractère...
 
pis je connais pas le langage que t'utilise, mais normalement faut utiliser une fonction, genre regexp.match() ou des trucs du style...faut déclarer le pattern, et ensuite appeler la fonction qui va le tester sur une chaine...
 
tu devrais relire la doc des regexp dans ton langage...
 
Dernier point : si  s/a/o/g est ta regexp, tu la trouveras jamais dans coucou, ca va ? ...


---------------
Jubi Photos : Flickr - 500px
Reply

Marsh Posté le 23-07-2004 à 10:42:35    

bein, je lui demande de remplacer les "a" par des "o". Normalement c'est correct.
 
je n'ai pas écrit "s/^a$/o/g" qui là, effectivement, ne serais pas correct car essayant de matcher la chaine entière.
 

Citation :

heu, une regexp c tjs une chaine de caractère...


 
oui, mais en perl, visiblement, on ne peut pas mettre la définition de la regex dans une chaine de caractère.
 
Voila du code qui marche par ex :
 

Code :
  1. my $chaine="coucou, ca va ?";
  2. $chaine=~s/a/o/g;
  3. print "chaine : $chaine\n";


Reply

Marsh Posté le 23-07-2004 à 10:46:13    

hum d'accord, c un script perl...je comprenais pas la syntaxe de ta regexp sinon...


---------------
Jubi Photos : Flickr - 500px
Reply

Marsh Posté le 23-07-2004 à 10:48:53    

Ha oui, dsl, j'ai oublié de préciser que c'est du Perl dont il s'agit :)

Reply

Marsh Posté le 23-07-2004 à 11:05:18    

Jubijub a écrit :

hum d'accord, c un script perl...je comprenais pas la syntaxe de ta regexp sinon...


 
c pas compliqué pourtant :/
 
substitute le a pour le o sur tout la chaine

Reply

Marsh Posté le 23-07-2004 à 11:08:25    

Perl ne peux pas reconnaitre que ta chaine est une regexp: pour lui c'est une simple chaine de caracteres.
Par contre il y a un quote special pour faire ce que tu veux: qr
 
regarde "Regexp Quote-Like Operators" dans perlop
http://www.perldoc.com/perl5.6/pod [...] -Operators
(cherche l'exemple avec le qr)
 
par contre dans tous les cas ca ne fera que la premiere partie de ta regexp (avec les eventuels modifieus genre i m g....) et pour la substitution tu devra toujours la faire toi meme
 
dong dans ton cas va pourrais donner un truc du genre:
 

Code :
  1. my $chaine="coucou, ca va ?";
  2.   my $regex= qr/a/i;
  3.  
  4.   $chaine= s/$regexp/o/g;
  5.   print "chaine : $chaine\n";


 
(en fait le modifieur g n'est pas non plus compilé dans la regexp, les seuls qui le soient sont i m s et x il me semble)
 
 
en fait ca t'avance pas beaucoup!
mais tu peux peut etre regarder du coté du modfieur e (exectution de la partie droite de la regexp)...
 
pourkoi tu veux faire ca en fait?


Message édité par pospos le 23-07-2004 à 11:08:53
Reply

Marsh Posté le 23-07-2004 à 11:31:19    

Merci, je vais regarder.
En fait, ce n'est pas moi particulièrement qui veux faire ca mais le prog que je modifie (j'ai horreur de ca)
 
C'est un préprocesseur de texte du nom de filepp, qui est en license GNU je crois, librement modifiable pour ses propres besoins. Je suis entrain de rajouter un opérateur de préprocessing du type #include qui s'apelle #regex et qui permet d'éxécuter une regex sur un #define préalablement défini.
(Je suis clair là ? :))
 
Bref, ma regex a appliquer se situe dans le fichier préprocéssé => je lis ca dans une chaine de caractère, c'est pourquoi je VEUX pouvoir appliquer cette regex, dont la définition, exacte du point de vue Perl, se situe dans cette chaine de caractère.

Reply

Marsh Posté le 23-07-2004 à 11:34:39    

Au passage je précise que malheureusement, cette "fonctionnalité" super simple à première vue, m'est demandée par mon boss :(  
 
J'ai horreur de perdre mon temps sur des conneries pareilles mais bon, j'ai interet à trouver :)
 
Si je trouve pas, je vais etre obligé de me tapper une analyse "à la mimine" de ma regex ... ce qui est con car je vais utiliser des regex pour ca ... (situation cocasse, non ?)


Message édité par oliv5 le 23-07-2004 à 11:37:43
Reply

Marsh Posté le 23-07-2004 à 11:46:10    

Burgergold a écrit :

c pas compliqué pourtant :/
 
substitute le a pour le o sur tout la chaine


 
J'y connais rien en Perl...et le s/x/y je sais vaguement que cun truc qui fait des remplacement (sur linuxfr on voit tjs les gars mettre : s/gratuit/libre ou des machins du genre)
 
mes regexp en java je les utilise comme ca :  
- déclaration du pattern
- test des matches du pattern sur un string donnée...


---------------
Jubi Photos : Flickr - 500px
Reply

Marsh Posté le 23-07-2004 à 11:46:10   

Reply

Marsh Posté le 23-07-2004 à 12:07:12    

oliv5, j'ai pas trop compris ton histoire, mais je pense que tu aurais interet à utiliser la meme logique que pour les trucs de template HTML
 
ansi par exemple si dans ton fichier re gegexp tu a des truc genre

Code :
  1. bidule  machin
  2. truc   chose

pour dire que tu veux remplacer tous les bidules par des machins et les trucs par des choses, alors tu peux les mettres dans une hash table qui aurait cette gueulle:

Code :
  1. %hash = (
  2.   bidule => 'machin',
  3.   truc => 'chose',
  4. );

(tu vois comment faire pour constituer cette hash automatiquement à partir du fichier?)
 
et ensuite tu fais comme ca:
 

Code :
  1. my $detection = join('|', keys(%hash));
  2. my $detection _rq = rq/($detection)/; # avec eventuellement un modifieur i par exemple si tu veux
  3. # puis plus tard
  4. $texte =~ s/$detection _rq/$hash{$1}/e;


 
explications:
$dectection va contenir toutes les clé de ta hash (donc bidule et truc) separés par des '|', et donc former une regexp qui acceptera tous ces clés.
on préconplie la regexp pour accelerer les futurs match, et en mettant des parenthses pour capturer le resultat dans $1.
 
ensuite on match et, grace au modifieur e, Perl va exectuer la partie de droite à chaque match : au lieu de faire un simple replace c'est le resultat de l'exectution qui sera inseré. Donc ici on remplace la clé trouvé par sa valur dans la hash


Message édité par pospos le 23-07-2004 à 12:07:34
Reply

Marsh Posté le 23-07-2004 à 13:06:30    

interessant, je ne connaissais pas le modifieur eet son utilisation dans ce cadre.
 
Mais, le pb de ta solution est qu'elle suppose qu'on connaisse à l'avance les mots clés recherchés, ce qui n'est pas le cas. Je cherche à autoriser l'utilisateur à executer n'importe qu'elle regex.
Voici un exemple de fichier parsé par le préprocesseur :
(ca marche un peu comme le préprocesseur du compilateur C)
 

Code :
  1. #define MACRO "valeur de ma macro"
  2. MACRO
  3. #regex MACRO "s/a/o/"
  4. MACRO


 
doit me donner en sortie :

Code :
  1. "valeur de ma macro"
  2. "voleur de ma macro"


 
la regex est complètement inconnue au moment de l'écriture du prog perl. Il va lire le fichier d'entrée (ligne par ligne) et interpréter les commandes "#machin". L'instruction "#regex" doit permettre d'executer la regex spécifiée ("s/a/o/" ) sur la valeur de la macro "MACRO".
 
Dans le perl, aprés analyse et reconnaissance du mot clé #regex, je me retrouve avec le nom de ma macro, sa valeur, et la définition de la regex dans 3 chaines de caractères : $macro, $valeur, $regex.
 
j'essaie de faire $macro=~$regex; mais effectivement ca n'est pas reconnu comme une regex. Ca ne plante pas, ni ne genere de message de warning ou d'erreur, ca ne fait rien !

Reply

Marsh Posté le 23-07-2004 à 13:49:19    

et d'autres regexps doivent pouvoir etre ajouté au cours de l'execution (c'est à dire apres que d'autres match aient étés faits?). Dans ce cas tu ne peux pas utiliser de regexp précompilée (c'est à dire avec qr// ou le modifieur o) mais sinon le principe est le meme:
 
tu parse ton fichier à la recherche de ton "regexp, et tu decoupe la regexp pour remplir ton hash. et à chaque fois tu refait ta variable "$detection"
tu vois le principe?
 
le mieux d'ailleur serait d'avoir un truc de la forme:
 
#regexp MACRO "a" "o" "modifieurs..."
 
plutot qu'une vraie regexp avec une syntaxe Perl
 
bon, sinon t'as toujours le moyen de t'en sortir avec un eval bien sauvage mais bon...

Reply

Marsh Posté le 23-07-2004 à 14:59:27    

pospos a écrit :

et d'autres regexps doivent pouvoir etre ajouté au cours de l'execution (c'est à dire apres que d'autres match aient étés faits?). Dans ce cas tu ne peux pas utiliser de regexp précompilée (c'est à dire avec qr// ou le modifieur o) mais sinon le principe est le meme:
 
tu parse ton fichier à la recherche de ton "regexp, et tu decoupe la regexp pour remplir ton hash. et à chaque fois tu refait ta variable "$detection"
tu vois le principe?
 
le mieux d'ailleur serait d'avoir un truc de la forme:
 
#regexp MACRO "a" "o" "modifieurs..."
 
plutot qu'une vraie regexp avec une syntaxe Perl
 
bon, sinon t'as toujours le moyen de t'en sortir avec un eval bien sauvage mais bon...


 
Tout a fait, c'est ce que je me suis résigné a faire mais c'est chiant car il me faut prévoir toutes les operations possibles : match, substitution, translation ...
 
Je suis obligé de matcher ma regexp comme ca : if ($regex=~/s\/(.+)\/(.*)\//) { traitement de la subsitution ... }
 
idem pour le tr et le simple matching (qui retourne 0 ou 1 dans le fichier de sortie selon que le match a lieu ou pas) ...
 
Ca marchotte, mais je trouve ca sale. Quitte à faire sale, autant faire un eval comme tu dis. Je trouve meme ca mieux car plus simple. Je vais essayer, merci, j'y avais pas pensé.

Reply

Marsh Posté le 23-07-2004 à 15:23:16    

et t'es obligé de l'entrer sous la forme d'une regexp Perl donc? c'est ton boss qui impose ca?
 
(d'aillerus tu entends koi par "translation"? transileration?)
 
Bon si vraiment c'est le cas alors un petit eval serait peut etre mieux en fait...
 
genre:

Code :
  1. my $chaine="coucou, ca va ?";
  2.   my $regex="s/a/o/g";
  3.  
  4.   eval "\$chaine =~ $regex";
  5.   print "chaine : $chaine\n";


 
ca devrait rouler (non testé) mais ca fait toujours chier de faire des evals...
 
et si tu veux pouvoir matcher d'autres variable que $chaine tu la copie simplement dans $chaine et tu fais l'inverse en sortie

Reply

Marsh Posté le 24-07-2004 à 16:56:18    

pospos a écrit :

et t'es obligé de l'entrer sous la forme d'une regexp Perl donc? c'est ton boss qui impose ca?
 
(d'aillerus tu entends koi par "translation"? transileration?)
 
Bon si vraiment c'est le cas alors un petit eval serait peut etre mieux en fait...
 
genre:

Code :
  1. my $chaine="coucou, ca va ?";
  2.   my $regex="s/a/o/g";
  3.  
  4.   eval "\$chaine =~ $regex";
  5.   print "chaine : $chaine\n";


 
ca devrait rouler (non testé) mais ca fait toujours chier de faire des evals...
 
et si tu veux pouvoir matcher d'autres variable que $chaine tu la copie simplement dans $chaine et tu fais l'inverse en sortie


 
J'ai fait avec l'eval. ca marche pour le match, mais pas pour la substitution. L'eval renvoie une erreur comme quoi il ne reconnait pas la commande.
 

Code :
  1. eval '("ok" =~ /"ok"/)'

renvoie 1
 

Code :
  1. eval '("ok" =~ s/"ok"/"ko"/)'

renvoie une erreur
 
Bah, c'est pas grave, j'ai implémenté un truc un peu limité qui fait juste les "match" et les "substitutions".
Sinon, le terme translation designe l'operation "tr" de remplacement d'un caractère (ie qubstitution d'un caractere).

Reply

Marsh Posté le 24-07-2004 à 16:56:49    

Merci au fait pour votre aide.

Reply

Marsh Posté le 24-07-2004 à 18:52:08    

bizarre, pourtant je viens de lancer l'exemple que je t'avais filé et ca marche nikel?
pourkoi met tu des guillemet partout?
 
tiens, voila une fonction generique qui implemente ca:

Code :
  1. sub exec_regexp {
  2. eval "\$_[0] =~ $_[1]";
  3. }
  4. my $chaine="coucou, ca va ?"; 
  5. exec_regexp($chaine, "s/a/o/g" );
  6. print "chaine : $chaine\n";


 
donc tu lui passe simplement ta chaine en premier argument, et al regexp en second argument, et ta chaine est modifiée 'in place'


Message édité par pospos le 24-07-2004 à 18:52:21
Reply

Marsh Posté le 24-07-2004 à 19:51:14    

Les guillemets c'est parce que ma chaine de test est "ok" avec les guillemets, c'est tout :) j'aimerais avoir "ko" en sortie, avec les guillements.
 
Sinon, je ne peux pas tester ca ici (ben je suis pas au boulot ^^). Sinon, je suis quasiment certain que ce que j'ai écrit ne marchait pas. Mais je l'ai ré-ecrit de tete. Malheureusement je peux pas vérifier ce que j'ai reellement ecrit.
 
J'essaierais texto-mano ce que tu as ecrit en esperant que ca marche.
 
Ha, aussi, je tourne sous Solaris, est-il possible que l'implementation de eval sous solaris soit différente de celle sous Linux ou sous Windows ?
 
Sinon, les regex ne me sont pas imposées du tout, c'est juste que je suis en charge du maintien et des evolutions des differents outils de developpement (hardware pour etre précis) et que donc, je suis amené à faire de petits developpements/scripts suivant les besoins des equipes de dev. Or, ca m'a été demandé par beaucoup de monde car c'est pratiue et, pour moi, ca evite de developpé 50 fonctions différentes dont les effets sont couverts par les regex...
Puis, découvrant le Perl, j'etais curieux de faire ca aussi :)

Reply

Marsh Posté le 24-07-2004 à 20:04:16    

oui ca marche pareil sur toutes les plateformes ou Perl marche
 
normalement la fonction que je t'ai filé marchera sans prob (je l'ai testé), faut juste que tu garde en tete que ca va modifier ta chaine "in place": ca ne te renvoi pas une copie modifiée, ca modifie directement la chaine, un peu comme les passages par reference en C.

Reply

Marsh Posté le 24-07-2004 à 20:15:00    

Ok, réponse lundi.
 
Sinon, derniere question : l'eval du perl n'apelle pas la fonction eval du shell ? Je croyais que si, mais je n'avais pas pensé au cas des os non-unix.
 
merci.

Reply

Marsh Posté le 24-07-2004 à 20:36:40    

non c'est l'interpreteur Perl qui va evaluer le string fourni entre "" à la volée.
 
D'ailleurs ca me fait penser que si ta regexp contient une erreur il faudrait pouvoir rattraper et afficher le message d'erreur pour pouvoir modifier la regexp (dans le cas present si la regexp n'est pas valable il ne se passe rien)
 
genre comme ca:
 

Code :
  1. sub exec_regexp {
  2. eval "\$_[0] =~ $_[1]" || warn;
  3. }
  4. my $chaine="coucou, ca va ?";
  5. my $regexp =  "s/a/o/g";
  6. exec_regexp($chaine, $regexp);
  7. print "chaine : $chaine\n";


ca va afficher le message d'erreur si la regexp et foireuse. Tu peux meme mettre un die à la place du warn pour carrement arreter l'execution (c'est d'aillerus ce qui se passerait si al regexp etait dans le script)

Reply

Marsh Posté le 26-07-2004 à 15:14:38    

Cool, ca fonctionne !
 
Merci pospos, je viens d'apprendre a me servir de cette @#{[# fonction "eval". En fait, j'avais oublié le "\" devant le premier $ et sans le warn, je n'avais aucun message et aucun resultat.
 
Excellent, voila qui va faire avancer les choses et surtout, me décharger d'une masse considérable de travail :D (bon, j'éxagère un peu, mais des fonctions penibles comme le pattern matching, je préfère ne pas avoir à en coder).  
 
Je prépare ma future réplique :  
- "Faites des regexp !!!"
- "Partout ?"
- "Oui, partout :ange: "
- "Ca marche pas"
- "Tu t'es trompé dans ta regex, recommences, c'est pas moi" :D  
 
Encore une fois merci

Reply

Marsh Posté le 26-07-2004 à 15:38:15    

A noter cependant :
Ta fonction ne marche pas pour le simple "matching" de patterns.
Je l'ai modifiée pour cela, de manière à recevoir 0 ou 1 selon que le pattern est reconnu dans la chaine.
J'ai viré le "warn", car l'eval renvoie 0 (pattern non reconnu dans la chaine) et le warning s'active alors.
Mais je ne reconnais plus les regex erronnées, mais a la rigueur, ca ne me gène pas, ca génère une erreur fatale (indiquant le numéro de ligne dans le fichier parsé) et l'execution ne vas pas plus loin, ce qui est ce que je souhaite.

Reply

Marsh Posté le 26-07-2004 à 15:57:44    

oui, en faite j'ai fait n'importekoi!
 
le warn est executé si la derniere instruction (une seule ici) du eval retourne false. Si il y a une erreur le eval retrounera effectivement false, mais ca sera aussi le cas si tu fait un match qui ne match pas, ou meme une substitution qui ne remplace rien!
A la limite ou pourrait s'en sortir en rajoutant un 1; à la fin de l'eval (derniere instruction toujours vraie) et on ne catcherait que les erreur, mais comme tu veux aussi recuperer le resultat des matchs ca ne te suffira pas.
 
De plus par defaut warn et die affichent le contenu de la variable d'erreur $!. Or l'eval a sa propre variable d'erreur $@, donc deja il faudrait faire 'warn $@'. $@ contient le message que tu pourra afficher pour l'utilisateur (peut etre autrement qu'avec un warn ou un die) pour qu'il puisse debuger sa regexp.
 
Donc le mieux pour tester si la regexp à foirer est de tester si $@ contient qqchose, et de retourner effectivement le resutlat de la regexp (match ou pas) :
 
 

Code :
  1. sub exec_regexp {
  2. my $match = eval "\$_[0] =~ $_[1]";
  3. if ($@) {
  4.  # erreur dans la regexp
  5.  warn $@;
  6.  return 0;
  7. }
  8. return $match;
  9. }
  10. my $chaine="coucou, ca va ?";
  11. my $regexp =  "s/a/o/g";
  12. exec_regexp($chaine, $regexp);
  13. print "chaine : $chaine\n";


Message édité par pospos le 26-07-2004 à 16:08:06
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

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