[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.
    0
    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
      0
      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
        1. Modérateur
          Salut,

          Si j'ai bien compris, quelque soit le nombre de champs tu veux récupérer juste les 2 premiers?
          0
          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
            0
          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 "/\|/";
            0
          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
            ...
            
            0
        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
          0
          1. Modérateur
            Re,

            Tu n'as pas répondu à ma question ;-)
            0
        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
          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
            
            0
          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
            0