IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Perl Discussion :

Multiple captures par regexp ?


Sujet :

Langage Perl

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre très actif

    Homme Profil pro
    Responsable projets techniques
    Inscrit en
    Février 2003
    Messages
    980
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Responsable projets techniques
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Février 2003
    Messages : 980
    Par défaut Multiple captures par regexp ?
    Bonjour à tous,

    voilà mon petit souci. J'ai un fichier comportant sur chaque ligne un certain nombre de champs se suivant séparés par un espace et définis ainsi :
    <NOM_CHAMP>:<VALEUR>
    Je ne connais pas à l'avance les noms des champs, ni leur nombre. Les valeurs peuvent prendre à peu près n'importe quelle forme (alphanumérique, quelques symboles y compris espace et deux-points...).

    Mon but est, vous l'aurez compris, de recréer un tableau plus classique à partir de ce fichier (au format type csv par exemple).

    On peut prendre les règles suivantes quand même (pour simplifier un peu - voir script) :
    1. pas de point-virgule ou de guillemets ou de caractères trop "chiants" =)
    2. un nom de champ commence par une lettre et ne contient que des caractères alphanumériques ou underscore
    3. conclusion de 1 & 2 : je repère chaque champ en trouvant un espace suivi d'une lettre éventuellement plusieurs caractères alphanumériques ou underscore et deux-points

    J'ai réussi à faire un script qui fonctionne plutôt bien MAIS qui me pose problème au traitement du dernier champ de la ligne ^^ :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    #!/usr/bin/perl
     
    use strict;
     
    open (FIC_E, "dsv.txt") or die ("dsv.txt : $!");
    open (FIC_S, ">dsv.csv") or die ("dsv.csv : $!");
     
    my $result;
    my @keys;
    my $nbrow=0;
     
    while (<FIC_E>) {
    	my @matches = m/(?:([a-zA-Z]\w+)\:(?:(.*?)\s?)(?=[a-zA-Z]\w+\:))/g;
     
    	next if ($#matches <= 0);
    	for (my $i=0; $i <= $#matches+1; $i+=2) {
    		push (@{$result->{$matches[$i]}}, $matches[$i+1]);
    		push (@keys,$matches[$i]) if ($nbrow == 0);
    	}
    	if (/([a-zA-Z]\w+)\:$/) {
    		push (@{$result->{$1}}, "");
    		push (@keys,$1) if ($nbrow == 0);
    	}
    	$nbrow++;
    }
     
    foreach my $key (@keys) {
    	print FIC_S $key,";";
    }
    print FIC_S "\n";
    for (my $i=0; $i < $nbrow; $i++) {
    	foreach my $key (@keys) {
    		print FIC_S '"',$result->{$key}->[$i],'";';
    	}
    	print FIC_S "\n";
    }
     
    close FIC_E;
    close FIC_S;
    La partie intéressante ici est bien entendu ma regexp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #m/(?:
    #	([a-zA-Z]\w+)\: ==> définition d'un nom de champ : une lettre + alphanum ou underscore...
    #	(?:(.*?)\s?) ==> capture des valeurs correspondantes : le \s? est là pour virer le dernier espace (oui, un champs peut n'être composé que d'espaces)
    #	(?=[a-zA-Z]\w+\:) ==> je m'arrête quand je détecte un nouveau nom de champ
    #)/g
    Il y a peut-être (sûrement) mieux ? Quoiqu'il en soit, ça marche bien comme je l'ai dit, sauf pour le dernier champ puisqu'il ne trouve pas de nouveau nom de champ à la suite

    Exemple
    mon entrée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    VAR:10 ID:0000000001 DATE: 2008-04-19 14:12:55
    VAR: 1 ID:           DATE: 2008-04-19 14:13:15
    VAR:10 ID:0000000009 DATE: 2008-04-19 14:14:32
    ma sortie:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    VAR;ID;
    "10";"0000000001";
    " 1";"          ";
    "10";"0000000009";
    ce que je voudrais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    VAR;ID;DATE;
    "10";"0000000001";" 2008-04-19 14:12:55";
    " 1";"          ";" 2008-04-19 14:13:15";
    "10";"0000000009";" 2008-04-19 14:14:32";
    merci =)

  2. #2
    Membre éprouvé
    Avatar de Jasmine80
    Femme Profil pro
    Bioinformaticienne
    Inscrit en
    Octobre 2006
    Messages
    3 157
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 45
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Bioinformaticienne
    Secteur : Santé

    Informations forums :
    Inscription : Octobre 2006
    Messages : 3 157
    Par défaut
    Si ton but n'est que d'écrire les données dans un fichier de sortie, à ta place, je ferai plus simple, je ne passerai pas par un tableau récupérant les résultats.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    #!/usr/bin/perl
     
    use strict;
    use warnings;
     
     
    # créations des filehandles
    open my $fic_e, '<', 'dsv.txt' or die "dsv.txt : $!";
    open my $fic_s, '>', 'dsv.csv' or die "dsv.csv : $!";
     
     
     
    while (my $line = <$fic_e>) {
     
    	# format des lignes à récupéréer
    	# VAR:10 ID:0000000001 DATE: 2008-04-19 14:12:55
    	if ($line =~ m/^\s*([A-Z]+)\:\s*(.*?)\s*([A-Z]+)\:\s*(.*?)\s*([A-Z]+)\:\s*(.*?)$/){
     
    		# on imprime les entêtes au premier passage dans la boucle
    		if ($. == 1){
    			#VAR;ID;DATE;
    			print $fic_s "$1;$3;$5;\n";
    		}
    		#"10";"0000000001";" 2008-04-19 14:12:55";
    		print $fic_s qq{"$2";"$4";"$6";\n};	
    	}
    	else{
    		warn $line;
    	}
    }
     
    # fermeture des filehandles 
    close $fic_e;
    close $fic_s;



    La sortie est bien :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    "10";"0000000001";"2008-04-19 14:12:55";
    " 1";"          ";"2008-04-19 14:13:15";
    "10";"0000000009";"2008-04-19 14:14:32";
    PS : j'ai volontairement supprimé l'espace devant la date mais tu peux facilement le récupérer via l'EXPREG si besoin est.

  3. #3
    Membre éprouvé
    Avatar de Jasmine80
    Femme Profil pro
    Bioinformaticienne
    Inscrit en
    Octobre 2006
    Messages
    3 157
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 45
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Bioinformaticienne
    Secteur : Santé

    Informations forums :
    Inscription : Octobre 2006
    Messages : 3 157
    Par défaut
    Voila pour le cas général avec un nombre variables de champs (je me suis rendue compte en relisant, que le problème était plus compliqué).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    #!/usr/bin/perl
     
    use strict;
    use warnings;
     
     
    # créations des filehandles
    open my $fic_e, '<', 'dsv.txt' or die "dsv.txt : $!";
    open my $fic_s, '>', 'dsv.csv' or die "dsv.csv : $!";
     
    my @recup;
     
     
    while (my $line = <$fic_e>) {
     
     
    	# format des lignes à récupéréer
    	# VAR:10 ID:0000000001 DATE: 2008-04-19 14:12:55	
    	while ( $line =~ m/([A-Z]+)\:\s?([^(:?A-Z+:)]+)/g){ 
     
    		# récupération des entêtes lors de la lecture
    		# de la première ligne
    		if($.==1){
    			print $fic_s "$1;";
    		}
    		push ( @{$recup[$.]}, $2 );	
    	}
    }
     
     
     
    # impression ligne par ligne
    for my $i (1..$#recup){
    	print $fic_s "\n";
    	map {print $fic_s qq{"$_";}} @{$recup[$i]};
    }
     
     
    # fermeture des filehandles 
    close $fic_e;
    close $fic_s;

    VAR;ID;DATE;
    "10 ";"0000000001 ";"2008-04-19 14";
    "1 ";" ";"2008-04-19 14";
    "10 ";"0000000009 ";"2008-04-19 14";
    Il reste un problème au niveau du dernier champ composé de 2 parties. Seule la première est récupérée.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $line =~ m/([A-Z]+)\:\s?([^(:?A-Z+:)]+)/g
    Je voudrais que dans le second couple de parenthèses on récupère tout ce qui n'est pas un groupe de majuscules suivi de deux points ... l'ennui est que la négation porte soit sur un groupe de majuscules, soit sur deux points mais pas sur le groupement. La récupération de $2 s'arrête donc dès qu'on tombe sur un groupe de majuscules ou sur 2 points et non quand on rencontre des majuscules suivies de 2 points. Qui sait comment résoudre ce problème? Merci.

  4. #4
    Membre très actif

    Homme Profil pro
    Responsable projets techniques
    Inscrit en
    Février 2003
    Messages
    980
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Responsable projets techniques
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Février 2003
    Messages : 980
    Par défaut
    Alors, déjà, merci pour tes réponses : je n'ai pas tout relu, mais je vais jeter un oeil =)

    Pour le moment, j'ai trouvé une solution toute conne hier soir : j'ai ajouté 2 petits caractères qui ont tout changé (enfin, 6 avec les parenthèses non capturantes :p) au niveau de la détection de la fin d'un champ !

    J'ai modifié la regexp ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #m/(?:
    #	([a-zA-Z]\w+)\: ==> définition d'un nom de champ : une lettre + alphanum ou underscore...
    #	(?:(.*?)\s?) ==> capture des valeurs correspondantes : le \s? est là pour virer le dernier espace (oui, un champs peut n'être composé que d'espaces)
    #	(?=[a-zA-Z]\w+\:) ==> je m'arrête quand je détecte un nouveau nom de champ
    #)/g
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #m/(?:
    #	([a-zA-Z]\w+)\: ==> définition d'un nom de champ : une lettre + alphanum ou underscore...
    #	(?:(.*?)\s?) ==> capture des valeurs correspondantes : le \s? est là pour virer le dernier espace (oui, un champs peut n'être composé que d'espaces)
    #	(?=(?:[a-zA-Z]\w+\:)|$)) ==> je m'arrête quand je détecte un nouveau nom de champ OU la fin de la ligne !
    #)/g
    J'ai aussi viré mon second test qui devait récupérer le dernier champ quand il était vide. Je vais regarder tes réponses (et ta question :p) : il y a encore des trucs que je ne connais pas (map, qq et $.). Merci encore

  5. #5
    Membre très actif

    Homme Profil pro
    Responsable projets techniques
    Inscrit en
    Février 2003
    Messages
    980
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Responsable projets techniques
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Février 2003
    Messages : 980
    Par défaut
    Pour répondre à ta question Jasmine80, je ne vois rien d'autre que ce que j'ai utilisé : à savoir tout capturer jusqu'à ce qu'on rencontre des majuscules suivies de 2 points.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $line =~ m/([A-Z]+)\:\s?(.*?)(?=(?:[A-Z]+\:|$))/g
    Je ne sais pas comment dire de capturer tout ce qui n'est pas majuscules suivies de 2 points, donc je lui demande de tout capturer jusqu'à trouver des majuscules suivies de 2 points...

  6. #6
    Membre éprouvé
    Avatar de Jasmine80
    Femme Profil pro
    Bioinformaticienne
    Inscrit en
    Octobre 2006
    Messages
    3 157
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 45
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Bioinformaticienne
    Secteur : Santé

    Informations forums :
    Inscription : Octobre 2006
    Messages : 3 157
    Par défaut
    Ton problème est-il donc résolu?

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Captures multiples dans une regexp
    Par Sp4MaFauTe dans le forum Langage
    Réponses: 1
    Dernier message: 28/03/2012, 18h01
  2. Multiple jointures par clés étrangères
    Par ymoreau dans le forum Langage SQL
    Réponses: 7
    Dernier message: 01/07/2009, 14h29
  3. Recherche par regexp dans Visual Studio
    Par Tuani dans le forum Visual Studio Team System
    Réponses: 0
    Dernier message: 05/03/2009, 14h37
  4. Problème multiplication sinus par data sur 640 bits
    Par wesleymec dans le forum Simulink
    Réponses: 4
    Dernier message: 25/09/2008, 15h58
  5. Réponses: 1
    Dernier message: 09/10/2006, 19h40

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo