[PERL expreg] capture optionnelle ou EOL

Résolu
Bonjour,

Je cherche à capturer un élément de manière optionnel :

A1F712492D9B|info_46943_0_11111@domaine.com
C55657BC738|info_48894_0_22222@domaine.com|20100826_121900


aujourd'hui je fais :
while (<FICHIER_HOLD>) {
	(($clef,$valeur) = /([^|]+)\|(.*)$/i);
}

qui me prend bien clef et valeur selon le |, mais je souhaiterais ajouter un élément optionnel correspondant à une date heure (ligne 2 de l'exemple) présente ou non après un |, j'ai bien bricoler un peu mais sans succès :

([^|]+)\|([^|]+)\|([^|]+)


pourtant j'ai bien lu le bouquin sur les expressions régulières ... ça doit pas être sorcier ...

Merci d'avance pour votre aide !

Stéphane

6 réponses

  1. Essaye Avec un regex de type ([^|]+)\|([^|]+)(?:\|([^|]+))?
    (?: ) = parenthèse non capturantes.
    1. je viens de tester avec The Regex Coach et ça ne semble pas faire ce que j'attends.

      si je test sur une ligne avec uniquement 2 éléments, il poursuit sur la seconde ligne pour finir le second token.

      si je test sur une ligne avec 3 éléments, il va finir le 3eme token sur la ligne suivante ....

      au secours ...

      Stéphane
      1. Après quelques essai je te propose ceci : ([^|]+)\|([^|\s]+)(?:\|([^\s]+))?
        Cela devrait marcher si tes champs entre pipe ne contiennent pas d'espaces, sinon ca va être plus compliqué...
        1. Modérateur
          Salut,

          Si j'ai bien compris, quelque soit le nombre de champs tu veux récupérer juste les 2 premiers?
          1. bonjour lami,

            non, je veux récupérer soit 2 champ soit 3 si le 3eme est présent.

            2 formats possible pour chaque ligne :

            champ1|champ2
            ou
            champ1|champ2|champ3

            quand le champ3 n'existe pas, la variable vaut rien ou vide :

            (($clef,$valeur,$datehold) = ([^|]+)\|([^|\s]+)(?:\|([^\s]+))?

            ça me parait pas mal ....

            Stéphane
          2. Modérateur
            Re,

            Pour ce genre de situation split doit faire l'affaire vu qu'il y a un séparateur de champs.
            Perso c'est comme ça que j'aurais fait (même si j'aime bien les regex ;-)

            ($clef,$valeur,$datehold) = split "/\|/";
          3. ça marche pas .... pourtant c'est plus logique et simple que l'expression régulière ....

            while (<FICHIER_HOLD_FROM>) {
                    ($clef,$valeur,$datehold) = split "/\|/";
            	print "clef : $clef  valeur : $valeur  datehold : $datehold\n";
            }
            


            donne :

            Use of uninitialized value in concatenation (.) or string at /repertoire/fichier.pl line 112, <FICHIER_HOLD> line 1.
            Use of uninitialized value in concatenation (.) or string at /repertoire/fichier.pl line 112, <FICHIER_HOLD_FROM> line 1.
            clef : C55657BC738|infos_00000_0_111111@domaine.com
              valeur :   datehold :
            
            


            avec la source :

            # cat fichier_hold.dat
            C55657BC738|infos_00000_0_111111@domaine.com
            ...
            
        2. parfais, non il n'y aura pas d'espace, j'aurais jamais pensé à utilisé l'espace !

          bravo

          maintenant je test en live sur un fichier ... monstrueux :)

          a+

          Stéphane
          1. Modérateur
            Re,

            Tu n'as pas répondu à ma question ;-)
        3. Modérateur
          Re,

          Oups, j'ai fait une erreur. Quand on mets les "" alors split considére qu'il s'agit d'une chaine de caractères. Donc c'est /\|/ et pas "/\|/"

          Ensuite il faut juste faire un test pour print comme ça plus des warnings.

          ~ $ cat champs_underscore.pl
          #!/usr/bin/perl  
          use strict;use warnings;  
          
          while (<DATA>) {  
            my ($clef,$valeur,$datehold) = split /\|/;  
            if($datehold){  
              print "clef : $clef  valeur : $valeur  datehold : $datehold\n";  
            }else{  
              print "clef : $clef  valeur : $valeur\n";  
            }  
          }  
          
          __END__  
          A1F712492D9B|info_46943_0_11111@domaine.com  
          C55657BC738|info_48894_0_22222@domaine.com|20100826_121900  
          
          ~ $ perl champs_underscore.pl 
          clef : A1F712492D9B  valeur : info_46943_0_11111@domaine.com  
          
          clef : C55657BC738  valeur : info_48894_0_22222@domaine.com  datehold : 20100826_121900


          GNU/Linux:Linux is Not Ubuntu! Quel linux choisir ne veut pas dire votre Distribution préférée,
          106485010510997108
          1. Modérateur
            Re,

            Tu peux aussi utiliser l'opérateur ?: si tu veux, pour faire plus court ;-)

            #!/usr/bin/perl
            use strict;use warnings;
            
            while (<DATA>) {
              my ($clef,$valeur,$datehold) = split /\|/;
              $datehold
              ?
                print "clef : $clef  valeur : $valeur  datehold : $datehold\n"
              :
                print "clef : $clef  valeur : $valeur\n"
              ;
            }
            
            __END__
            A1F712492D9B|info_46943_0_11111@domaine.com
            C55657BC738|info_48894_0_22222@domaine.com|20100826_121900
            
          2. excellent, le print n'est là que pour faire jolie, je vais stocker en tableau uniquement les entrées de moins de 2 jours et donc purger les autres.
            Je vais donc parser la datehold et la matcher, DateTime est mon ami !

            Merci beaucoup à tout les 2 !!

            Stéphane