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 :

problème : Traitement d'une chaine


Sujet :

Langage Perl

  1. #1
    Nouveau Candidat au Club
    Inscrit en
    Novembre 2005
    Messages
    5
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5
    Points : 1
    Points
    1
    Par défaut problème : Traitement d'une chaine
    Bonjour,

    voila mon problème : j'ai une chaine de caractère :
    B.TOTO, C.TA_TA, SUBSTR(A.TUTU,1,4)!!'-'!!SUBSTR(A.TETE,5,2)!!'-'!!SUBSTR(A.TYTY,7,1),B.TITI
    et j'aimerai la couper au niveau des "," de façon à récupérer dans un tableau :

    B.TOTO
    C.TA_TA
    SUBSTR(A.TUTU,1,4)!!'-'!!SUBSTR(A.TETE,5,2)!!'-'!!SUBSTR(A.TYTY,7,1)
    B.TITI

    mon problème c'est que la fonction split sépare même au niveau des virgules dans les parenthèses ce que je ne veux pas ...
    j'ai beau chercher je ne vois pas comment faire pouvez vous m'aider s'il vous plait ?

    merci.

  2. #2
    Membre éprouvé Avatar de MarneusCalgarXP
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    911
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 911
    Points : 1 118
    Points
    1 118
    Par défaut
    question : y a-t-il un espace après chaque virgule que tu veux utiliser comme séparateur ?

    Je ne répond à aucune question technique par MP.

    Si votre problème est réglé, n'oubliez pas Dans tous les cas

  3. #3
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Peux-tu nous garantir qu'il n'y aura qu'un seul niveau de parenthèse au plus ?

    --
    Jedaï

  4. #4
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par MarneusCalgarXP
    question : y a-t-il un espace après chaque virgule que tu veux utiliser comme séparateur ?
    Pas dans son exemple...

    --
    Jedaï

  5. #5
    Nouveau Candidat au Club
    Inscrit en
    Novembre 2005
    Messages
    5
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    @ MarneusCalgarXP : Non, il peut y avoir un espace comme il peut ne pas y en avoir et il en va de même dans les parenthèses.

    @ Jedai : Non il peut très bien y avoir SUBSTR(SUBSTR(A.TUTU,1,4),5,10) ...

    PS: A noter que dans la parenthèse on peut aussi trouver MAX(TO_TO,TUTU), la virgule à l'intérieur d'une parenthèse n'est pas forcément suivi d'un chiffre

    PS2: Les fonctions sont fausses bien entendu mais c'est à titre d'exemple ...

  6. #6
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par hitsugaya
    @ Jedai : Non il peut très bien y avoir SUBSTR(SUBSTR(A.TUTU,1,4),5,10) ...
    Désolé, mais dans ce cas ton problème est trop complexe pour de vraies regex (ton langage n'est pas régulier), il pourrait être réglé avec les regex de Perl, mais je te le déconseille car la complexité nécessaire rendra ton programme difficile à comprendre... Perl 5.10 permettra de faire ça bien plus facilement.
    Mieux vaut écrire un véritable Parser pour l'instant. Par exemple avec Parse::RecDescent :
    Code Perl : 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
    #!/usr/bin/perl
    use strict; use warnings;
     
    use Parse::RecDescent;
     
    my $grammar = q {
    line: statement(s /, */) /\n?/      { $return = [ @{$item[1]} ] }
     
    statement: expr(s)                   { $return = join '', @{$item[1]} }
     
    expr: notcomma paren(?)              { $return = @{$item[2]} ? $item[1].$item[2][0] : $item[1] }
     
    paren: '(' expr(s? /,/) ')'          { $return = '('.(join ',', @{$item[2]}).')' }
     
    notcomma: /[^,()\n]+/
    };
     
    my $text = "B.TOTO, C.TA_TA, SUBSTR(SUBSTR(A.TUTU,1,4),1,4)!!'-'!!SUBSTR(A.TETE,5,2)!!'-'!!SUBSTR(A.TYTY,7,1),B.TITI\n";
     
    my $parser = Parse::RecDescent->new($grammar) or die "Bad grammar!\n";
     
    defined( my $ret = $parser->line($text) ) or die "Bad text!\n";
     
    __END__

    --
    Jedaï

  7. #7
    Membre éprouvé Avatar de MarneusCalgarXP
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    911
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 911
    Points : 1 118
    Points
    1 118
    Par défaut
    Citation Envoyé par Jedai
    Pas dans son exemple...
    C'est la raison pour laquelle je lui demandais, au cas où il s'agirait d'un problème de copier coller pour l'exemple...

    Je ne répond à aucune question technique par MP.

    Si votre problème est réglé, n'oubliez pas Dans tous les cas

  8. #8
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par MarneusCalgarXP
    C'est la raison pour laquelle je lui demandais, au cas où il s'agirait d'un problème de copier coller pour l'exemple...
    C'eut été une bénédiction !

    La solution avec regex :
    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
    use re 'eval';
    my $expr;
    $expr = qr{
    [^,()\n]+
    (?:
      \( 
        (?:
          (?> [^,()]+ )
        | (??{ $expr })
        )?
        (?:
          (?> ,[^,()]+ )
        | (,??{ $expr })
        )*
      \)
    )?
    }x;
     
    my $statement = qr{$expr*};
     
    my @line;
    $text =~ m/($statement)/g;
    my $first = $1;
    push @line, $first;
    while( $text =~ m/\G, *($statement)/g ) {
        push @line, $1;
    }
    A vous de voir quel est la plus simple et la plus maintenable (sans parler du fait que (?? { }) n'est pas très très stable je crois).

    --
    Jedaï

  9. #9
    Nouveau Candidat au Club
    Inscrit en
    Novembre 2005
    Messages
    5
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Merci pour l'aide !!

    Entre temps j'ai réussi à trouver une feinte (idiote mais bon ca marche quand même ) pour faire ce que je voulais. Je la mets au cas où quelqu'un aurait le même problème :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    my $separateur = "§";
     
    $cpt = 0;
    for(0..length($texte)){
    	$cpt++ if((substr($texte,$_,1) eq "(")==1);
    	$cpt-- if((substr($texte,$_,1) eq ")")==1);
     
            if((substr($texte,$_,1) eq ",")==1 and $cpt == 0){
    	        $texte = substr($texte,0,$_) . $separateur . substr($texte,$_+1, length($texte));
            }
    }
    En fait c'est tout bête, je comptabilise dans $cpt le nombre de parenthèse ouvrante rencontrées, je décrémente $cpt quand je rencontre une parenthèse fermante. Et je "remplace" la virgule lorsque j'en rencontre une et que $cpt est à 0.

    Vila ça à l'air de marcher ... (j'ai pas rencontré d'erreur pour l'instant -Croise les doigts ^^ - )

    Encore merci à vous pour m'avoir aidez bye

  10. #10
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    C'est une bonne solution algorithmiquement parlant, j'avais envisagé d'en parler avant pour t'en dissuader, parce qu'en Perl pur implémenter cette solution est particulièrement lent... surtout si comme toi on mets des substr() partout.
    Par ailleurs cette solution ne te protège pas des parenthésages incorrects (tu devrais vérifier que $cpt ne devient pas négatif, et te plaindre dans ce cas).

    Au moins utilise une variable temporaire et évite les "(_ eq _) == 1" inutiles :
    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
        my $in = 0;
        my @line;
        my $start = 0;
        for(0..length($text) - 1){
            my $char = substr $text, $_, 1;
            $in++ if $char eq '(';
            $in-- if $char eq ')';
            die "Parenthésage incorrect\n" if $in < 0;
     
                if( $char eq ',' and not $in){
                    push @line, substr $text, $start, $_ - $start;
                    $start = $_ + 1;
                }
        }
        push @line, substr $text, $start;
     
    # si tu veux récupérer ta ligne avec de nouveaux séparateur :
    my $newline = join '§', @line;
    --
    Jedaï

  11. #11
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Ca m'apprendra à faire le malin... Après Benchmark de l'ensemble des solutions, on aboutit au résultat suivant :
    • J'avais lu que Parse::RecDescent était lent... Il n'est pas lent, il est simplement EXTREMEMENT lent. Recalé !! (42 fois plus lent que ta solution)
    • Ta solution est largement suffisante si tu n'as pas des quantités de données gigantesques à traiter (8000 lignes par seconde sur ma machine)
    • Ma solution dans la même idée est légèrement plus rapide, très légèrement
    • Le moteur de regex nous écrase tous, avec dans les 35000 lignes par seconde sur ma machine !!


    Donc substr() n'est pas si lent, le moteur de regex est néanmoins beaucoup plus rapide, et P::RD est à proscrire si l'on a besoin de vitesse (il traitait tout de même 200 lignes par seconde sur ma machine).

    --
    Jedaï

  12. #12
    Nouveau Candidat au Club
    Inscrit en
    Novembre 2005
    Messages
    5
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Merci Jedai! Au niveau de la quantité de données, j'ai dans les 17 000-18 000 lignes à traiter et ça peut (et va) encore augmenter donc j'pense tester et utiliser ta solution avec le regex . Je repasserai demain dans la journée pour te dire si c'est efficace sur ma machine avec la quantité de donnée que j'ai ...

    Encore merci et à demain bye

  13. #13
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par hitsugaya
    Merci Jedai! Au niveau de la quantité de données, j'ai dans les 17 000-18 000 lignes à traiter et ça peut (et va) encore augmenter donc j'pense tester et utiliser ta solution avec le regex . Je repasserai demain dans la journée pour te dire si c'est efficace sur ma machine avec la quantité de donnée que j'ai ...

    Encore merci et à demain bye

    Sur ma machine (un portable pas mauvais mais pas non plus une machine de guerre), la solution par regex mets 0,5 secondes pour lire et parser ligne à ligne un fichier de 18000 lignes, ta solution mets dans les 2,3 secondes. Evidemment tout dépend des traitements que tu infliges par la suite à ces lignes, mais ça devrait aller niveau performance.

    --
    Jedaï

  14. #14
    Nouveau Candidat au Club
    Inscrit en
    Novembre 2005
    Messages
    5
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Salut, alors je viens de tester la solution avec regex et bien que ca soit plus rapide que ma solution (beaucoup plus rapide meme ) j'ai une erreur quand il arrive sur une ligne comme :

    C.PERIODE, A.NO_CD, B.RS_CD, D.CP_AD_POST, A.UNT_CD ,MAX(A.CD_C1),MAX((A.CD_C1 * 2.6316) / 100), MAX(A.CD_C2),MAX((A.CD_C2 * 1.5544) / 100), MAX(A.CD_C3),MAX((A.CD_C3 * 1.0152) / 100), MAX(A.CD_C1 + A.CD_C2 + A.CD_C3), MAX(((A.CD_C1 * 2.6316) / 100) + ((A.CD_C2 * 1.5544) / 100) + ((A.CD_C3 * 1.0152) / 100))

    alors pour t'expliquer ... quand il arrive à la virgule avant la partie en gras il prend le MAX d'après mais pas ce qui suit (partie en gras) ... et je comprends pas pourquoi ...

    je débute en perl alors j'ai du mal à comprendre ton expression :

    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
     
    my $expr;
    $expr = qr{
    [^,()\n]+
    (?:
      \( 
        (?:
          (?> [^,()]+ )
        | (??{ $expr })
        )?
        (?:
          (?> ,[^,()]+ )
        | (,??{ $expr })
        )*
      \)
    )?
    }x;
    my $statement = qr{$expr*};
    que contient ou doit contenir $expr ??
    comment faire pour régler le petit problème qui est survenu ?

    merci, bye

  15. #15
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    88
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 88
    Points : 70
    Points
    70
    Par défaut
    Bonjour,

    Pour ma culture personnelle, j'ai essaie aussi de comprendre le regex de Jedai.. mais j'avoue que j'ai rien compris.

    J'aimerai bien aussi avoir une explication... si c'est possible.

    Merci

  16. #16
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Oui, désolé, ma regex était légèrement faible pour un type d'expression aussi généraliste...
    Voici une correction qui marche sur ta ligne et qui est suffisamment générale je pense pour marcher à peu près dans tous les cas, elle est aussi légèrement plus rapide :
    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
    use re 'eval';
    our $expr;
    $expr = qr{
    (?:
    (?> [^,()\n]* )
    (?:
      \( 
        (??{ $expr })(?: , (??{ $expr }))*?
      \)
    )?
    )+?
    }x;
     
    my @line;
    $text =~ m/($expr)/g;
    my $first = $1;
    push @line, $first;
    while( $text =~ m/\G, *($expr)/g ) {
        push @line, $1;
    }
    Quant à l'explication...
    Voici une version commentée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    use re 'eval';
    our $expr;
    $expr = qr{
    (?:
    (?> [^,()\n]* )  # 0 ou plus caractères différents de (),\n sans backtracking
    (?:
      \(  # parenthèse ouvrante
        (??{ $expr }) # première sous-expression
        (?: , (??{ $expr }))*? # 0 ou plus paresseusement sous-expression séparée par des ,
      \)  # parenthèse fermante
    )?  # il n'y a pas forcément de parenthèses dans une expression
    )+? # 1 ou plus, paresseusement
    }x;
    La clé là-dedans, c'est (??{ $expr }), une construction qui appelle au moment de l'exécution $expr et en fait une regex, c'est très différent de mettre $expr tout simplement car dans ce cas, l'interpolation se fait à la compilation, et non récursivement, ce qui ne permet pas de gérer un nombre indéfini de niveau de parenthèses.

    N'hésitez pas à poser des questions ! Notez par ailleurs que la 5.10 va entre autre améliorer nettement le moteur de regex pour rendre ce type de solution bien plus facile et élégant à mettre en place. Vous avez un sujet là-dessus en haut de ce forum.

    --
    Jedaï

    --
    Jedaï

Discussions similaires

  1. [FLASH 8] traitement d'une chaine de caractère
    Par KaiN_974 dans le forum Flash
    Réponses: 17
    Dernier message: 12/06/2006, 12h42
  2. Réponses: 2
    Dernier message: 21/12/2005, 17h23
  3. Traitement d'une chaine
    Par Kerod dans le forum Langage
    Réponses: 9
    Dernier message: 11/09/2005, 01h55
  4. traitement d'une chaine de caractère
    Par mohamed dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 09/12/2004, 18h45
  5. Réponses: 3
    Dernier message: 21/06/2004, 12h20

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