[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. 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
      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
    2. 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. 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