Forum |  HardWare.fr | News | Articles | PC | Prix | S'identifier | S'inscrire | Aide | Shop Recherche
1611 connectés 

  FORUM HardWare.fr
  Programmation
  Perl

  [Perl] simplifier ma regex

 



 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

[Perl] simplifier ma regex

n°2270830
Sethenssen
Posté le 05-12-2015 à 17:42:50  profilanswer
 

Hello,
 
Voici ma ligne à parser:

Code :
  1. 2015/12/05 12:55:56.217 RmCmdSuccess,Cmd=CreateSession,RID=172.21.205.31,CmdTime=7ms,SesId=0025643d7ecb/269123690,chan0=22,MPN=274,BW=2475007,GDA=0.0.0.0,UDP=56100,Tid=447885331


 
Mon but est d'obtenir ça:

Code :
  1. 20151205125556|0025643d7ecb/269123690|2475007|0.0.0.0|LOG-01


 
J'y arrive avec ce code ci-dessous

Code :
  1. if (/Cmd=CreateSession/) {
  2.        print $_, "\n";
  3.        $_ =~ s/^([^R]*)\ ([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*)(.*)$/$1|$6|$9|$10|LOG-01/g;
  4.        $_ =~ s/SesId=|BW=|GDA=|:|\ //g;
  5.        my ($startdate, $session, $bitrate, $gda, $srm, $mls) = split(/\|/, $_);
  6.        $startdate =~ s/\///g;
  7.        ($startdate, $mls) = split(/\./, $startdate);
  8.        print "$startdate|$session|$bitrate|$gda|$srm", "\n";
  9.        last;
  10. }


 
Mais je ne trouve pas ma regex super jolie et je voudrai arriver à récupérer mes variables sans les nettoyer (ce que je fais en ligne 4. et 6. et 7.)
Est-ce qu'il est possible de faire plus simple et plus propre?
 
Merci d'avance.

mood
Publicité
Posté le 05-12-2015 à 17:42:50  profilanswer
 

n°2270860
gilou
Modérateur
Modzilla
Posté le 06-12-2015 à 12:51:58  profilanswer
 

Voila une version plus directe, au vu de tes données:

Code :
  1. #!/usr/bin/perl
  2. use Modern::Perl;
  3.  
  4. $_ = '2015/12/05 12:55:56.217 RmCmdSuccess,Cmd=CreateSession,RID=172.21.205.31,CmdTime=7ms,';
  5. $_ .= 'SesId=0025643d7ecb/269123690,chan0=22,MPN=274,BW=2475007,GDA=0.0.0.0,UDP=56100,Tid=447885331';
  6. my $result = '20151205125556|0025643d7ecb/269123690|2475007|0.0.0.0|LOG-01';
  7.  
  8. if (m|^(\d\d\d\d)/(\d\d)/(\d\d)\s(\d\d):(\d\d):(\d\d)\.\d+.+,Cmd=CreateSession,.+,SesId=([^,]+),.+,BW=([^,]+),GDA=([^,]+),|) {
  9.  print "$1$2$3$4$5$6|$7|$8|$9|LOG-01\n";
  10.  print "$result\n"; # pour comparer
  11. }


 
J'ai choisi |...| pour encadrer la regex plutôt que l'habituel /.../ parce qu'il y a des / dans la chaîne à parser (et que je suppose qu'il n'y aura pas de | dans cette chaîne).
 
si tu veux que ta regexp soit plus lisible, avec le modifier x, on peut la mettre sur plusieurs lignes:

Code :
  1. if (m|^(\d\d\d\d)/(\d\d)/(\d\d)\s(\d\d):(\d\d):(\d\d)\.\d+.+,
  2.        Cmd=CreateSession,
  3.        .+,
  4.        SesId=([^,]+),
  5.        .+,
  6.        BW=([^,]+),
  7.        GDA=([^,]+),|x) {
  8.  print "$1$2$3$4$5$6|$7|$8|$9|LOG-01\n";
  9.  print "$result\n"; # pour comparer
  10. }


 
Mais bon, une manière bien plus logique de procéder est la suivante:

Code :
  1. #!/usr/bin/perl
  2. use Modern::Perl;
  3.  
  4. $_ = '2015/12/05 12:55:56.217 RmCmdSuccess,Cmd=CreateSession,RID=172.21.205.31,CmdTime=7ms,';
  5. $_ .= 'SesId=0025643d7ecb/269123690,chan0=22,MPN=274,BW=2475007,GDA=0.0.0.0,UDP=56100,Tid=447885331';
  6. my $result = '20151205125556|0025643d7ecb/269123690|2475007|0.0.0.0|LOG-01';
  7.  
  8. if (/Cmd=CreateSession/) {
  9.  my @fields = split /,/;
  10.  my $time = shift @fields;
  11.  $time =~ s|^(\d\d\d\d)/(\d\d)/(\d\d)\s(\d\d):(\d\d):(\d\d)\.\d+.+|$1$2$3$4$5$6|o;
  12.  my %hash = map {if (/(.+)=(.+)/) {($1, $2)}} @fields;
  13.  print "$time|$hash{SesId}|$hash{BW}|$hash{GDA}|LOG-01\n";
  14.  print "$result\n";
  15. }


Si on repère une ligne à traiter: if (/Cmd=CreateSession/)
On splitte les champs selon la virgule: my @fields = split /,/;
Ils sont tous de la forme XX=YYY sauf le premier.
On colle le premier champ dans une variable a part: my $time = shift @fields;
On colle les autres dans un hash, un champ de la forme XX=YYY fournissant un élment du hash de clé XX et de valeur YYY: my %hash = map {if (/(.+)=(.+)/) {($1, $2)}} @fields;
On extrait du premier champ de qui nous intéresse: $time =~ s|^(\d\d\d\d)/(\d\d)/(\d\d)\s(\d\d):(\d\d):(\d\d)\.\d+.+|$1$2$3$4$5$6|o;
qu'on envoie en sortie avec les valeurs du hash pour certaines clés: print "$time|$hash{SesId}|$hash{BW}|$hash{GDA}|LOG-01\n";
et c'est tout, zou!
 
Note:
J'ai écrit
my %hash = map {if (/(.+)=(.+)/) {($1, $2)}} @fields;
ça marche parce que je suis certain que tous les champs sont de la forme XX=YYY
Si je n'en suis pas sur, faire
my %hash;
map {if (/(.+)=(.+)/) {$hash{$1}=$2} @fields;
qui marche à tout les coups et ne tient compte que des champs de la bonne forme XX=YYY et saute ceux qui sont d'une autre forme.
En y repensant, un  
my %hash = grep /./, map {if (/(.+)=(.+)/) {($1, $2)}} @fields;  
fera la même chose (les champs d'une autre forme, qui renvoient undef avec map sont éliminés par le grep) et est un one liner.
 
Après, si on veut faire plus compact et moins lisible et maintenable, on peut toujours faire tout avec un hash unique:

Code :
  1. if (/Cmd=CreateSession/) {
  2.  my %hash = map {if (/(.+)=(.+)/) {($1, $2)}
  3.           elsif (m|^(\d\d\d\d)/(\d\d)/(\d\d)\s(\d\d):(\d\d):(\d\d)\.\d+.+|) {('TimeStamp', "$1$2$3$4$5$6" )}} split /,/;
  4.  print "$hash{TimeStamp}|$hash{SesId}|$hash{BW}|$hash{GDA}|LOG-01\n";
  5.  print "$result\n";
  6. }


 
On peut rendre ça encore plus compact

Code :
  1. #!/usr/bin/perl
  2. use Modern::Perl;
  3.  
  4. $_ = '2015/12/05 12:55:56.217 RmCmdSuccess,Cmd=CreateSession,RID=172.21.205.31,CmdTime=7ms,';
  5. $_ .= 'SesId=0025643d7ecb/269123690,chan0=22,MPN=274,BW=2475007,GDA=0.0.0.0,UDP=56100,Tid=447885331';
  6. my $result = '20151205125556|0025643d7ecb/269123690|2475007|0.0.0.0|LOG-01';
  7.  
  8. my $date_regex = qr|^(\d\d\d\d)/(\d\d)/(\d\d)\s(\d\d):(\d\d):(\d\d)\.\d+.+|;
  9.  
  10. if (/Cmd=CreateSession/) {
  11.  print join('|', grep(/./, map {if (/^(SesId|BW|GDA)=(.+)/o) {"$2"}
  12.                      elsif (m/$date_regex/o) {"$1$2$3$4$5$6"}} split(/,/, $_))), "|LOG-01\n";
  13.  print "$result\n";
  14. }


 
Et si on veut, quitte a laisser tomber $result et a se placer dans le cadre de ton utilisation réelle (lecture en boucle ligne à ligne d'un log), on peut même aboutir à un one liner, guru-level illisible.
 

Code :
  1. my $timestamp = qr|^(\d\d\d\d)/(\d\d)/(\d\d)\s(\d\d):(\d\d):(\d\d)\.\d+.+|;
  2. ...
  3. while (<$fh> ) {
  4.    print join('|', grep(/./, map {if (/^(SesId|BW|GDA)=(.+)/o) {"$2"}
  5.                                    elsif (m/$timestamp/o) {"$1$2$3$4$5$6"}}
  6.                                  split(/,/, $_))), "|LOG-01\n" if (/Cmd=CreateSession/);
  7. }


 
En ce qui me concerne, je ferais ceci:

Code :
  1. my $date_regex = qr{(\d\d\d\d)/(\d\d)/(\d\d)\s(\d\d):(\d\d):(\d\d)\.\d+};
  2. ...
  3. if (/Cmd=CreateSession/) {
  4.  my ($timestamp, @fields) = split /,/;
  5.  $timestamp =~ s/^$date_regex .*/$1$2$3$4$5$6/o;
  6.  my %hash = grep /./, map {if (/(.+)=(.+)/) {($1, $2)}} @fields;
  7.  print "$timestamp|$hash{SesId}|$hash{BW}|$hash{GDA}|LOG-01\n";
  8. }


Parce que c'est lisible, compréhensible (pour qui pratique un peu Perl), et surtout, flexible et réutilisable: le jour ou tu veux imprimer une autre valeur de champ en sortie, par exemple celui associé à MPN, il te suffira de coller $hash{MPN} dans le print.
 
A+,


Message édité par gilou le 06-12-2015 à 16:02:51

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --    In umbra igitur pugnabimus. --
n°2270875
Sethenssen
Posté le 06-12-2015 à 19:42:01  profilanswer
 

Waouh !
Ça c'est de la réponse qui envoie du lourd  
Chapeau bas Mr Gilou  :jap:  
 
C'est très clair et en plus j'apprends des nouvelles choses  :)  
 
Merci


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  Perl

  [Perl] simplifier ma regex

 

Sujets relatifs
[PERL] CGI retourner un code erreurUrl Rewriting - Regex
e voudrais lancer plusieurs script Perl à partir d'un autre script Per[PERL] Problème regexp \1
[PERL] regex !!![RÉSOLU] yet another regex issue
[UNIX][PERL][KSH] ET logique bit à bit[Perl ] Comportement étrange avec DBI
Créer un exécutable à partir d'un script Perl 
Plus de sujets relatifs à : [Perl] simplifier ma regex


Copyright © 1997-2018 Hardware.fr SARL (Signaler un contenu illicite) / Groupe LDLC / Shop HFR