[PERL] regex !!!

regex !!! [PERL] - Perl - Programmation

Marsh Posté le 10-06-2015 à 14:31:30    

Bonjour,
 
Je m'arrache les cheveux avec mon faible niveau en perl et les regex ;)
 
Voila je lis un fichier qui contient par exemple les lignes suivante :
 
(salon on ou baie vitree on) et (cuisine on ou entree on)  
Salon on et (entree on ou cuisine on) et baie vitree on
((salon on ou entree on) et (baie vitree on et cuisine on)) ou chambre on
 
et je veux transformer ces lignes en :
 
(/salon on/ || /baie vitree on/) && (/cuisine on/ || /entree on/)
/salon on/ && (/entree on/ || /cuisine on/) && /baie vitree on/
((/salon on/ || /entree on/) && (/baie vitree on/ && /cuisine on/)) || /chambre on/
 
j'ai écris le code suivant en supposant que $cond contient les lignes du fichier, lues une par une dans une boucle.
 

Code :
  1. if ($cond=~/ ou /)
  2. {
  3. $cond=~s/ ou /\/ ou \//g;
  4. }
  5. if ($cond=~/\)\/ ou \/\(/)
  6. {
  7. $cond=~s/\)\/ ou \/\(/\) ou \(/g;
  8. }
  9. if ($cond=~/ et /)
  10. {
  11. $cond=~s/ et /\/ et \//g;
  12. }
  13. if ($cond=~/\)\/ et \/\(/)
  14. {
  15. $cond=~s/\)\/ et \/\(/\) et \(/g;
  16. }
  17. if ($cond=~/\)\/ et /)
  18. {
  19. $cond=~s/\)\/ et /\) et /g;
  20. }
  21. if ($cond=~/\/ et \/\(/)
  22. {
  23. $cond=~s/\/ et \/\(/\/ et \(/g;
  24. }
  25. $cond=~s/ et / && /g;
  26. $cond=~s/ ou / || /g;
  27. if ($cond =~ /\(/)
  28. {
  29. $cond=~s/\(/\(\//g;
  30. }
  31. if ($cond =~ /\(/)
  32. {
  33. $cond=~s/\)/\/\)/g;
  34. }
  35. if ($cond =~/^\w/)
  36. {
  37. $cond="/".$cond;
  38. }
  39. if ($cond =~ /\w$/)
  40. {
  41. $cond=$cond."/";
  42. }


 
Je n’excuse par avance pour ce code de débutant, mais c'est compliqué et ça ne marche pas !!!!
 
Quelqu'un peut m'aider et me montrer le chemin à suivre ?
 
Par avance merci.


Message édité par web_olivier le 10-06-2015 à 14:39:36
Reply

Marsh Posté le 10-06-2015 à 14:31:30   

Reply

Marsh Posté le 10-06-2015 à 17:45:18    

Déjà une astuce pour améliorer nettement la lisibilité: On peut utiliser un autre séparateur que '/' pour les regex, p.ex. s!ceci!cela!. Du coup on peut mettre des '/' directement sans devoir écrire "\/".

 

C'est peut-être pas élégant mais plus court et ça fonctionne. Par contre faut faire la liste des mots une fois pour le premier regex.

 
Code :
  1. use strict;
  2. use warnings FATAL => 'all';
  3.  
  4. my $texte=<<ENDT;
  5. (salon on ou baie vitree on) et (cuisine on ou entree on)  
  6. Salon on et (entree on ou cuisine on) et baie vitree on
  7. ((salon on ou entree on) et (baie vitree on et cuisine on)) ou chambre on
  8. ENDT
  9.  
  10.  
  11. foreach my $cond (split(/\n/,$texte))
  12. {
  13.     $cond =~ s!((?:salon|baie vitree|cuisine|chambre|entree) on)!/$1/!g;
  14.     $cond =~ s! ou ! || !g;
  15.     $cond =~ s! et ! && !g;
  16.     print $cond,"\n";
  17. }
  18.  
  19.  
  20. __END__
  21. (/salon on/ || /baie vitree on/) && (/cuisine on/ || /entree on/)  
  22. Salon on && (/entree on/ || /cuisine on/) && /baie vitree on/
  23. ((/salon on/ || /entree on/) && (/baie vitree on/ && /cuisine on/)) || /chambre on/
 

Ca sent la domotique...

Message cité 1 fois
Message édité par rat de combat le 10-06-2015 à 17:45:38
Reply

Marsh Posté le 10-06-2015 à 17:59:24    

Sans devoir faire la liste des mots, hautement expérimental!! (autrement dit à tester intensivement avant utilisation ou faire valider par un pro)

 
Code :
  1. $cond =~ s!(^|\(|ou |et )([\w ]+?) on!$1/$2 on/!g;


(remplace le premier regex)


Message édité par rat de combat le 10-06-2015 à 18:00:57
Reply

Marsh Posté le 10-06-2015 à 20:37:11    

La routine replace reçoit une ligne et renvoie la ligne modifiée

Code :
  1. my @data = ("(salon on ou baie vitree on) et (cuisine on ou entree on)  ",
  2.         "Salon on et (entree on ou cuisine on) et baie vitree on",
  3.         "((salon on ou entree on) et (baie vitree on et cuisine on)) ou chambre on " );
  4.  
  5. sub replace {
  6.  my $line = shift;
  7.  $line =~ s!^\s*!/!;
  8.  $line =~ s!\s*$!/!;
  9.  $line =~ s!(\(+)!$1/!g;
  10.  $line =~ s!(\)+)!/$1!g;
  11.  $line =~ s!\sou\s!/ || /!g;
  12.  $line =~ s!\set\s!/ && /!g;
  13.  $line =~ s!/\(!(!g;
  14.  $line =~ s!\)/!)!g;
  15.  return $line;
  16. }
  17.  
  18. my @replaced = map{replace($_)} @data;
  19. print join("\n", @replaced);


 
- D'abord je met des / en début et fin de ligne (si ils sont inutiles, ils seront effacés plus tard)
- Je remplace les (...( et )...) par (...(/ et /)...)
- Je remplace les ou et et par / || / et / && /
- Je vire les trucs qui devraient pas y être en remplaçant /( et )/ par ( et )
 
A+,


Message édité par gilou le 10-06-2015 à 22:21:48

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 10-06-2015 à 22:19:24    

Bonsoir,
 
Déjà merci à vous 2. :hello:  
 
Je regarde ça en détail et je vous tiens au courant.
Gilou merci pour les explications à venir :)

Reply

Marsh Posté le 10-06-2015 à 22:40:48    

Je viens de tester.
Gilou ça fonctionne parfaitement :love: :jap:  
Rat de combat, effectivement je triture ma domotique à la maison ;)  
Ta première solution fonctionne mais le script ne peut pas connaitre à l'avance le nom des capteurs. J'ai testé ta 2eme solution mais je me retrouve avec un / en trop au début et à la fin. Ceci dit chapeau quand même :jap:  
 
Un grand merci pour votre aide.

Reply

Marsh Posté le 10-06-2015 à 22:47:32    

On peut optimiser un peu le sub (ou en tout cas en réduire la verbosité) ainsi:
 

Code :
  1. sub replace {
  2.  $_ = shift;
  3.  s!^\s*!/!;
  4.  s!\s*$!/!;
  5.  s!(\(+)!$1/!g;
  6.  s!(\)+)!/$1!g;
  7.  s!\sou\s!/ || /!g;
  8.  s!\set\s!/ && /!g;
  9.  s!/\(!(!g;
  10.  s!\)/!)!g;
  11.  return $_;
  12. }


 
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 10-06-2015 à 23:02:50    

Une petite note pour le rongeur guerrier:

rat de combat a écrit :

Déjà une astuce pour améliorer nettement la lisibilité: On peut utiliser un autre séparateur que '/' pour les regex, p.ex. s!ceci!cela!. Du coup on peut mettre des '/' directement sans devoir écrire "\/".
 
C'est peut-être pas élégant mais plus court et ça fonctionne. Par contre faut faire la liste des mots une fois pour le premier regex.
 

Code :
  1. use strict;
  2. use warnings FATAL => 'all';
  3.  
  4. my $texte=<<ENDT;
  5. (salon on ou baie vitree on) et (cuisine on ou entree on)  
  6. Salon on et (entree on ou cuisine on) et baie vitree on
  7. ((salon on ou entree on) et (baie vitree on et cuisine on)) ou chambre on
  8. ENDT
  9.  
  10.  
  11. foreach my $cond (split(/\n/,$texte))
  12. {
  13.     $cond =~ s!((?:salon|baie vitree|cuisine|chambre|entree) on)!/$1/!g;
  14.     $cond =~ s! ou ! || !g;
  15.     $cond =~ s! et ! && !g;
  16.     print $cond,"\n";
  17. }
  18.  
  19.  
  20. __END__
  21. (/salon on/ || /baie vitree on/) && (/cuisine on/ || /entree on/)  
  22. Salon on && (/entree on/ || /cuisine on/) && /baie vitree on/
  23. ((/salon on/ || /entree on/) && (/baie vitree on/ && /cuisine on/)) || /chambre on/


 
Ca sent la domotique...

Plutôt qu'un ENDT que tu lis de manière complexe, utiliser __DATA__ serait sans doute plus direct ici:
 

Code :
  1. use strict;
  2. use warnings FATAL => 'all';
  3.  
  4. while (<DATA> ) {
  5.  s!((?:salon|baie vitree|cuisine|chambre|entree) on)!/$1/!g;
  6.  s! ou ! || !g;
  7.  s! et ! && !g;
  8.  print;
  9. }
  10.  
  11. __DATA__
  12. (salon on ou baie vitree on) et (cuisine on ou entree on)  
  13. Salon on et (entree on ou cuisine on) et baie vitree on
  14. ((salon on ou entree on) et (baie vitree on et cuisine on)) ou chambre on


 
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 10-06-2015 à 23:31:59    

Citation :

J'ai testé ta 2eme solution mais je me retrouve avec un / en trop au début et à la fin.


Je ne vois pas, où ça? Le résultat de mon script correspond à celui du script de gilou...?

 

Merci gilou pour le coup du __DATA__, je connaissais pas.

 
Citation :

rongeur guerrier


:D


Message édité par rat de combat le 10-06-2015 à 23:39:20
Reply

Marsh Posté le 11-06-2015 à 00:25:01    

Excuse rat de combat c'est moi qui faisais une erreur :(
Ta solution est donc encore plus "concentrée" :jap:
 
Merci à vous.

Reply

Marsh Posté le 11-06-2015 à 00:25:01   

Reply

Marsh Posté le 11-06-2015 à 11:08:55    

Euh, dans la solution de Rat de combat, il y a ce probleme: Salon n'est pas reconnu parce qu'il commence par une majuscule (début de la 2e ligne).
Il faut remplacer la ligne
s!((?:salon|baie vitree|cuisine|chambre|entree) on)!/$1/!g;
par
s!((?:salon|baie vitree|cuisine|chambre|entree) on)!/$1/!gi;
avec un i (case independent) pour régler le problème.
Et il vaut mieux mettre des \s+ dans ce genre d'expression:
s!((?:salon|baie\s+vitree|cuisine|chambre|entree)\s+on)!/$1/!gi;
A+,


Message édité par gilou le 11-06-2015 à 11:20:44

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 11-06-2015 à 17:14:53    

Merci Gilou, comme je ne connais pas le nom des capteurs il faut que j'utilise ta méthode ou la 2eme de rat de combat.
 
Je me permets de vous poser une autre question.
J'ai 2 tableaux, comment on peut les comparer ligne par ligne et trouver les lignes différentes (changement d'état).
 
Merci.

Reply

Marsh Posté le 11-06-2015 à 17:56:28    

En utilisant le module Text::Diff, je pense.
 
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Sujets relatifs:

Leave a Replay

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