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 :

[RegExp]Split chaîne par point virgule sauf ceux entre guillemets ?


Sujet :

Langage Perl

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Juillet 2017
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur intégration

    Informations forums :
    Inscription : Juillet 2017
    Messages : 6
    Par défaut [RegExp]Split chaîne par point virgule sauf ceux entre guillemets ?
    Bonjour,

    Pour le développement d'un script en Perl afin de rechercher dans un fichier csv (délimité par les points virgules), j'ai besoin de
    splitter une chaîne par le délimiteurs des points virgules pour récupérer des mots et expressions.

    MAIS je ne dois pas splitter si ces délimiteurs sont entre guillemets.

    Ex : une ligne du fichier -> un mot;"mon;expression";un_mot;autre_mot;"une autre;expression"
    Et comme résultat j'ai un tableau avec :
    [0]=> un mot
    [1]=>"mon;expression"
    [2]=>un_mot
    [3]=>autre_mot
    [4]=>"une autre;expression"

    (note : la conservation des guillemets est un plus).

    Je pensais utiliser les regexp.

    J'ai trouvé un cas similaire sur le forum PHP ( d'où j'ai copie l'exemple) mais malheureusement je n'y connais rien en PHP.

    Merci par avance de votre aide .

  2. #2
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Billets dans le blog
    1
    Par défaut
    Le plus simple est d'utiliser un module spécialisé du CPAN tel que Text::CSV.

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Juillet 2017
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur intégration

    Informations forums :
    Inscription : Juillet 2017
    Messages : 6
    Par défaut
    Merci Lolo78 de votre réponse , je suis débutant dans la programmation et je suis pas sûr de savoir utiliser le module CPAN Test::csv pour réaliser ce que je veux faire.
    Si vous avez une solution en expression régulière je suis preneur.

  4. #4
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Billets dans le blog
    1
    Par défaut
    Pour résoudre ce genre de problème, il faut un vrai parser (même simple), les expressions régulières ne peuvent résoudre ce genre de problème seules.

    Et l'utilisation d'un module comme Text::CSV n'est pas du tout compliquée. Lis la donc de ce module et n'hésite pas à demander si tu éprouves des difficultés.

  5. #5
    Responsable Perl et Outils

    Avatar de djibril
    Homme Profil pro
    Inscrit en
    Avril 2004
    Messages
    19 822
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 19 822
    Par défaut
    Bonjour,

    J'ai un doute sur la validité de ton fichier CSV de base à visu de ton exemple ci-dessus.

  6. #6
    Expert confirmé Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 349
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 349
    Par défaut
    Bonjour,

    Sinon, voici un exemple en regex one-liner:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    $ cat /tmp/xx.txt 
    un mot;"mon;expression";un_mot;autre_mot;"une autre;expression"
    un mot;"mon;expres;sion";un_mot;autre_mot;"une;autre;expression"
    un mot;"mon;expres;sion";un_mot;autre_mot;"une;autre;;expre;ssion"
    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
    $ perl -ne '$i=s/("[^";]*);([^"]*")/\1<pt-vgl>\2/g;$i=s/(<pt-vgl>[^";]*);/\1<pt-vgl>/ while($i);@XX=split /;/;map { s/<pt-vgl>/;/g } @XX;foreach $i (0..$#XX){ print "[$i]=>$XX[$i]\n"; } ' /tmp/xx.txt
    [0]=>un mot
    [1]=>"mon;expression"
    [2]=>un_mot
    [3]=>autre_mot
    [4]=>"une autre;expression"
     
    [0]=>un mot
    [1]=>"mon;expres;sion"
    [2]=>un_mot
    [3]=>autre_mot
    [4]=>"une;autre;expression"
     
    [0]=>un mot
    [1]=>"mon;expres;sion"
    [2]=>un_mot
    [3]=>autre_mot
    [4]=>"une;autre;;expre;ssion"
    Le code découpé:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    $i=s/("[^";]*);([^"]*")/\1<pt-vgl>\2/g;
    $i=s/(<pt-vgl>[^";]*);/\1<pt-vgl>/ while($i);
    @XX=split /;/;
    map { s/<pt-vgl>/;/g } @XX;
    foreach $i (0..$#XX){
      print "[$i]=>$XX[$i]\n";
    }

  7. #7
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Juillet 2017
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur intégration

    Informations forums :
    Inscription : Juillet 2017
    Messages : 6
    Par défaut
    Bonsoir disedorgue

    Merci pour votre réponse , j'ai essayé votre one-liner avec le fichier xx.txt fait comme dans votre exemple et j'ai ceci
    perl -ne '$i=s/("[^";]*);([^"]*")/\1<pt-vgl>\2/g;$i=s/(<pt-vgl>[^";]*);/\1<pt-vgl>/ while($i);@XX=split /;/;map { s/<pt-vgl>/;/g } @XX;foreach $i (0..$#XX){ print "[$i]=>$XX[$i]\n"; } 'xx.txt

    [0]=>


    [0]=>

    il n'y a pas de valeur associé à[0]

    J'ai tenté de comprendre votre regexp et j'ai un peu de mal ,pouvez vous m'expliquer :
    qu'est ce que signifie <pt-vgl> et que fait \1<pt-vgl>\2 ?
    $i=s/(<pt-vgl>[^";]*);/\1<pt-vgl>/ while($i); je ne comprends pas le while après les///

    Merci par avance de vos lumières.

  8. #8
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    disedorgue a en fait écrit un mini-parser artisanal. Essentiellement (en simplifiant un peu), voici que que ça fait.

    La première partie
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    $i=s/("[^";]*);([^"]*")/\1<pt-vgl>\2/g;
    recherche des guillemets, suivis de caractères autres qu'un guillemet ou un point-virgule, suivis d'un point-virgule, suivi de caractères autres d'un guillemet, suivis d'un guillemet et replace en définitive le point-virgule par la chaîne de caractère "<pt-vgl>".

    Donc, si tu as quelque chose du genre "mon;expression" dans ta chaîne de caractère, c'est remplacé par "mon<pt-vgl>expression".

    Une fois cela fait, on peut splitter la chaîne sur les points-virgules, puisque ceux qui restent ne sont plus dans des chaînes entre guillemets. Enfin, on peut remplacer les chaînes "<pt-vgl>" par des points-virgules.

    J'ai simplifié un peu l'explication, car ce que j'ai expliqué ne prendrait pas en compte le cas où il y a plusieurs points-virgules dans la même chaîne entre guillemets.

  9. #9
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Billets dans le blog
    1
    Par défaut
    Bonjour metrcg,

    j'ai repris ton code et l'ai reformaté sous la forme d'un vrai script (et non d'un uniligne). J'ai aussi ajouté use strict; use warnings;, ajouté les déclarations nécessaires de variables, remplacé les \1 et \2 par $1 et $2 car c'est la syntaxe recommandée aujourd'hui pour les variables de capture de regex. A priori, aucun de ces changements ne devrait modifier le fonctionnement du code, c'est juste un peu plus "propre".

    J'ai aussi mis les données de test en entrée dans une section __DATA__ pour ne pas avoir à créer un fichier supplémentaire.

    Ceci me donne le script suivant:
    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
     
    use strict;
    use warnings;
     
    while (<DATA>) {
        chomp;
        my $i = s/("[^";]*);([^"]*")/$1<pt-vgl>$2/g;         # premier point-virgule dans une chaîne entre guillemets
        $i = s/(<pt-vgl>[^";]*);/$1<pt-vgl>/ while ($i);     # points-virgules suivants dans une chaîne entre guillemets
        my @XX = split /;/;                                  # split sur les ; restants
        map { s/<pt-vgl>/;/g } @XX;                          # remplacement des chaînes <pt-vgl> par des ;
        foreach $i (0..$#XX){ 
            print "[$i]=>$XX[$i]\n"; 
        }
        print "\n";
    }    
    __DATA__
    un mot;"mon;expression";un_mot;autre_mot;"une autre;expression"
    un mot;"mon;expres;sion";un_mot;autre_mot;"une;autre;expression"
    un mot;"mon;expres;sion";un_mot;autre_mot;"une;autre;;expre;ssion"
    Et ça marche pour moi:
    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
     
    $ perl parse_csv.pl
    [0]=>un mot
    [1]=>"mon;expression"
    [2]=>un_mot
    [3]=>autre_mot
    [4]=>"une autre;expression"
     
    [0]=>un mot
    [1]=>"mon;expres;sion"
    [2]=>un_mot
    [3]=>autre_mot
    [4]=>"une;autre;expression"
     
    [0]=>un mot
    [1]=>"mon;expres;sion"
    [2]=>un_mot
    [3]=>autre_mot
    [4]=>"une;autre;;expre;ssion"
    Donc, le programme marche. Je pense que ton problème est dans ta syntaxe Linux. Essaie de remplacer 'xx.txt (à la toute fin) par ' xx.txt, c'est-à-dire d'ajouter un espace entre l'apostrophe marquant la fin du script uniligne et le nom du fichier en entrée. Vérifie aussi le contenu du fichier.

  10. #10
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Juillet 2017
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur intégration

    Informations forums :
    Inscription : Juillet 2017
    Messages : 6
    Par défaut
    Ça marche nickel ! merci Lolo78 pour les explications et effectivement le problème venait de l'espace manquant après l'apostrophe .
    Merci à tous de vos réponses, c'est la première fois que je me suis inscrit sur un forum et j'ai beaucoup apprécié l'entre-aide.

  11. #11
    Expert confirmé Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 349
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 349
    Par défaut
    Merci Lolo78 d'avoir fait le support de l'uniligne.
    Je vais essayer de perdre cette habitude d'utiliser des '\1' au lieu des '$1' mais comme je fais aussi beaucoup de sed, le naturel revient assez vite

  12. #12
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Juillet 2017
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur intégration

    Informations forums :
    Inscription : Juillet 2017
    Messages : 6
    Par défaut
    Bonjour,
    J'ai adapté le regexp à ma situation et je me suis confronté à un autre problème que je n'ai pas vu quand j'ai exposé mon problème.Je peux avoir des valeurs entre les guillemets sans avoir de point virgule, exemple la ligne suivante avec "la date jj_mm_aaaa".

    un mot;"la date jj_mm_aaaa";un_mot;un nombre;"mon;expression";autre_mot;"une autre;expression";la fin

    en faisant tourner le script de lolo78 j'obtiens:

    [0]=>un mot
    [1]=>"la date jj_mm_aaaa";un_mot;un nombre;"mon
    [2]=>expression";autre_mot;"une autre
    [3]=>expression"
    [4]=>la fin

    et je souhaiterais

    [0]=>un mot
    [1]=>"la date jj_mm_aaaa"
    [2]=>un_mot
    [3]=>un nombre
    [1]=>"mon;expression"
    [5]=>autre_mot
    [6]=>"une autre;expression"
    [7]=>la fin

    Merci par avance de votre aide.

  13. #13
    Expert confirmé Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 349
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 349
    Par défaut
    Voici un correctif (ce qui change en rouge) :
    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
    use strict;
    use warnings;
    
    while (<DATA>) {
        chomp;
        s/("[^";]*)(;?)([^"]*")/$1$2<pt-vgl>$3/g;                # premier point-virgule dans une chaîne entre guillemets
        s/([^;])<pt-vgl>/$1/g;                                   # suppression des faux positif <pt-vgl>
        my $i = s/;(<pt-vgl>)/$1/g;                              # suppression du ; lié a <pt-vgl>
        $i = s/(<pt-vgl>[^";]*);/$1<pt-vgl>/ while ($i);         # points-virgules suivants dans une chaîne entre guillemets
        my @XX = split /;/;                                      # split sur les ; restants
        map { s/<pt-vgl>/;/g } @XX;                              # remplacement des chaînes <pt-vgl> par des ;
        foreach $i (0..$#XX){
            print "[$i]=>$XX[$i]\n";
        }
        print "\n";
    }
    __DATA__
    un mot;"mon;expression";un_mot;autre_mot;"une autre;expression"
    un mot;"mon;expres;sion";un_mot;autre_mot;"une;autre;expression"
    un mot;"mon;expres;sion";un_mot;autre_mot;"une;autre;;expre;ssion"
    un mot;"monexpression";un_mot;autre_mot;"une;autre;;expre;ssion"
    Ce qui donne:
    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
    $ perl ./script.pl
    [0]=>un mot
    [1]=>"mon;expression"
    [2]=>un_mot
    [3]=>autre_mot
    [4]=>"une autre;expression"
     
    [0]=>un mot
    [1]=>"mon;expres;sion"
    [2]=>un_mot
    [3]=>autre_mot
    [4]=>"une;autre;expression"
     
    [0]=>un mot
    [1]=>"mon;expres;sion"
    [2]=>un_mot
    [3]=>autre_mot
    [4]=>"une;autre;;expre;ssion"
     
    [0]=>un mot
    [1]=>"monexpression"
    [2]=>un_mot
    [3]=>autre_mot
    [4]=>"une;autre;;expre;ssion"

  14. #14
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Juillet 2017
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur intégration

    Informations forums :
    Inscription : Juillet 2017
    Messages : 6
    Par défaut
    Ça marche impeccable ! même l'adaptation dans mon script.
    Heureusement que vous êtes là , trop content ... merci infiniment de votre aide

  15. #15
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 986
    Par défaut
    Au lieu de procéder à des substitutions entre les quotes puis de splitter, on peut chercher tous les champs:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    perl -nE'chomp;say join "\n",/\G([^";]*|"[^"]*")(?:;|$)/g' file.csv
    Ce que ça fait:
    • ça assure que tous les champs sont contiguës grâce à l'ancre \G et suivis soit d'un ; soit de la fin de ligne.


    Ce que ça ne fait pas:
    • ça ne gère pas les champs contenant des sauts de lignes puisque les lignes sont traitées une par une.
    • ça ne gère pas les quotes échappés, cela dit c'est facile à faire en changeant "[^"]*" par "[^"\\]*(?:\\.[^"\\]*)*" ou "[^"]*(?:""[^"]*)*" (suivant le mode d'échappement).
    • ça ne vérifie pas que le nombre de champs reste le même entre les enregistrements.

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

Discussions similaires

  1. [VBA-E] Convertir données separées par point virgule
    Par Elstak dans le forum Macros et VBA Excel
    Réponses: 12
    Dernier message: 31/01/2018, 12h05
  2. [MySQL] Alimenter un tableau par des string séparés par point virgule
    Par Zikas-r dans le forum PHP & Base de données
    Réponses: 5
    Dernier message: 16/04/2009, 11h19
  3. Import données délimités par point virgule
    Par bungle57 dans le forum SAS Base
    Réponses: 3
    Dernier message: 26/03/2009, 16h35
  4. [RegExp]Split chaîne par espaces sauf ceux entre guillemets ?
    Par Loic Desjardins dans le forum Langage
    Réponses: 2
    Dernier message: 17/11/2006, 10h33
  5. [RegExp] Exclure le caractère ";" (point virgule)
    Par yoyot dans le forum Général JavaScript
    Réponses: 9
    Dernier message: 23/06/2006, 17h31

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