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

C Discussion :

Bug strtok sur chaine de type x;y;;z


Sujet :

C

  1. #1
    Membre à l'essai
    Inscrit en
    Novembre 2003
    Messages
    34
    Détails du profil
    Informations forums :
    Inscription : Novembre 2003
    Messages : 34
    Points : 21
    Points
    21
    Par défaut Bug strtok sur chaine de type x;y;;z
    Salut et bonnes années à tous !

    Cette année commence fort avec un bug qui me perturbe et que je ne sais comment contrer !

    J'ai un programme C qui lit chaque ligne d'un fichier et décompose la ligne en plusieurs chainés (délimiteur :

    j'utilise donc strtok : field = strtok(line, ";")

    Cela fonctionne correctement jusqu'au moment ou j'utilise des cas particulier (des champs vides)

    Par exemple avec la ligne suivante : x;y;;z
    strtok(line, ";") => x OK !
    strtok(NULL, ";") => y OK !
    strtok(NULL, ";") => z KO !
    Il zappe totalement le 3ème champs parce que il est vide !

    Y a t-il un moyen d'éviter cela, y a t-il une autre fonction .... ?

    Merci.

  2. #2
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Remarque: strtok n'est pas conseillé comme fonction du fait qu'il modifie les arguments qu'on lui passe en argument...

    Ce code semble bien marcher:

    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
     
    #include <stdio.h>
    #include <string.h>
     
    int main&#40;&#41;
    &#123;
            char s&#91;32&#93; = "x;;;;;;;;;;;;y;;;;;;;;z;;a";
            char *ps;
            int i = 1;
            ps = strtok&#40;s,";"&#41;;
     
            while&#40;ps&#41;
            &#123;
     
            if&#40;ps&#41;
                    printf&#40;"%d&#41; %s\n",i,ps&#41;;
            i++;
            ps = strtok&#40;NULL,";"&#41;;
     
            &#125;
            printf&#40;"Fin\n"&#41;;
     
            return 0;
    &#125;
    Jc

  3. #3
    Rédacteur/Modérateur
    Avatar de Trap D
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    4 942
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 4 942
    Points : 6 498
    Points
    6 498
    Par défaut
    Si strtok ne fonctionne pas, simule le en utilisant strchr(str, ';'). au moins si ça ne marche pas ce sera de ta faute
    "La haine seule fait des choix" - Koan Zen
    "Il ne faut pas être meilleur que les autres, il faut être meilleur que soi." Albert Jacquard
    "Ceux qui savent où ils ont posé leur parapluie ne sont pas alcooliques." - pgibonne.
    Faites du Prolog, ça vous changera les idées !
    Ma page Prolog
    Mes codes sources commentés

    Mon avatar : La Madeleine à la veilleuse de Georges de La Tour

  4. #4
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut Re: Bug strtok sur chaine de type x;y;;z
    Citation Envoyé par CanardJM
    Par exemple avec la ligne suivante : x;y;;z
    strtok(line, ";") => x OK !
    strtok(NULL, ";") => y OK !
    strtok(NULL, ";") => z KO !
    Il zappe totalement le 3ème champs parce que il est vide !
    Oui, bug connu de strtok()... Essaye strsep() (C'est POSIX, donc relativement portable)
    Pas de Wi-Fi à la maison : CPL

  5. #5
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Euh, ça, je ne dirais pas que c'est un bug...
    Mais strtok() ne sert pas à séparer des champs, mais des tokens...
    il est ainsi normal qu'en utilisant l'espace comme séparateur, donne les mêmes résultats que (Seuls a et b doivent être reconnus comme tokens, pas l'espace vide...)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  6. #6
    Membre à l'essai
    Inscrit en
    Novembre 2003
    Messages
    34
    Détails du profil
    Informations forums :
    Inscription : Novembre 2003
    Messages : 34
    Points : 21
    Points
    21
    Par défaut
    Citation Envoyé par fearyourself
    Remarque: strtok n'est pas conseillé comme fonction du fait qu'il modifie les arguments qu'on lui passe en argument...

    Ce code semble bien marcher:

    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
     
    #include <stdio.h>
    #include <string.h>
     
    int main&#40;&#41;
    &#123;
            char s&#91;32&#93; = "x;;;;;;;;;;;;y;;;;;;;;z;;a";
            char *ps;
            int i = 1;
            ps = strtok&#40;s,";"&#41;;
     
            while&#40;ps&#41;
            &#123;
     
            if&#40;ps&#41;
                    printf&#40;"%d&#41; %s\n",i,ps&#41;;
            i++;
            ps = strtok&#40;NULL,";"&#41;;
     
            &#125;
            printf&#40;"Fin\n"&#41;;
     
            return 0;
    &#125;
    Jc

    Ce code ne marche pas il me renvoie :
    1) x
    2) y
    3) z
    4) a
    Fin

    Or je voudrai qu'il me renvoie
    1) x
    12) y
    19) z
    21) a
    Fin

  7. #7
    Membre à l'essai
    Inscrit en
    Novembre 2003
    Messages
    34
    Détails du profil
    Informations forums :
    Inscription : Novembre 2003
    Messages : 34
    Points : 21
    Points
    21
    Par défaut Re: Bug strtok sur chaine de type x;y;;z
    Citation Envoyé par Emmanuel Delahaye
    Oui, bug connu de strtok()... Essaye strsep() (C'est POSIX, donc relativement portable)
    Je n'ai pas strsep sur mon environnement Et ce n'est pas un environnement que je peux toucher comme ça !

  8. #8
    Membre à l'essai
    Inscrit en
    Novembre 2003
    Messages
    34
    Détails du profil
    Informations forums :
    Inscription : Novembre 2003
    Messages : 34
    Points : 21
    Points
    21
    Par défaut
    Citation Envoyé par Médinoc
    Euh, ça, je ne dirais pas que c'est un bug...
    Mais strtok() ne sert pas à séparer des champs, mais des tokens...
    ok donc en clair ce n'est pas ce qu'il me faut, il n'y a pas une fonction équivalente pour ce que je veux ?

  9. #9
    Membre expérimenté
    Inscrit en
    Décembre 2004
    Messages
    1 478
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 478
    Points : 1 664
    Points
    1 664
    Par défaut
    Il n'y a pas de fonction standard qui fasse ce que tu souhaites. Par contre, il est relativement facile d'en faire une (fonction - pas standard, evidemment).
    Voici ma solution:
    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
     
    int strsplit&#40;char *str, int token, char *words&#91;&#93;, int maxwords&#41;
    &#123;
      char *p = str;
      int nwords = 0;
     
      while&#40;1&#41;
      &#123;
        while&#40;*p == token&#41;
        &#123;
          p++;
        &#125;
     
        if&#40;*p == '\0'&#41;
        &#123;
          return nwords;
        &#125;
     
        words&#91;nwords++&#93; = p;
     
        while&#40;*p != token && *p != '\0'&#41;
        &#123;
          p++;
        &#125;
     
        if&#40;*p == '\0'&#41;
        &#123;
          return nwords;
        &#125;
     
        *p++ = '\0';
     
        if&#40;nwords >= maxwords&#41;
        &#123;
          return nwords;
        &#125;
      &#125;
    &#125;
    Cela s'utilise comme ca:
    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
     
    #include <stdio.h>
     
    #include "strsplit.h"
     
    #define MAXWORD 10
     
    int main&#40;void&#41;
    &#123;
      char str&#91;&#93; = "x;;;;;;;;;;;;y;;;;;;;;z;;a";
      char *words&#91;MAXWORD&#93;;
      int n;
      int nwords = strsplit&#40;str, ';', words, MAXWORD&#41;;
     
      if&#40;nwords&#41;
      &#123;
        for&#40;n=0; n<nwords; n++&#41;
        &#123;
          fprintf&#40;stderr, "%02d &#58; %s\n", words&#91;n&#93; - str + 1, words&#91;n&#93;&#41;;
        &#125;
      &#125;
     
      return 0;
    &#125;
    et on obtient bien en sortie:
    01 : x
    14 : y
    23 : z
    26 : a
    La fonction ci-dessus a quelques defauts (elle modifie la chaine passee en parametre, elle a besoin d'un nombre maximum de mot a ne pas depasser [histoire de ne pas avoir a malloc()/realloc() le tableau de mots]), mais elle fait son boulot...

  10. #10
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Tant qu'à faire des différence entre valeurs de pointeurs, j'ai une préférence pour passer en (void *) (histoire de lancer une nouvelle discussion)...

    Mais si tu le fais, pourquoi ne pas juste modifier mon code pour que ce soit comme ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
     if&#40;ps&#41;
                    printf&#40;"%d&#41; %s\n",&#40;&#40;int&#41; &#40;&#40;&#40;void *&#41; ps&#41; - &#40;&#40;void *&#41; s&#41;&#41;&#41; / &#40;sizeof&#40;char&#41;&#41;+1,ps&#41;;
    Jc

    PS: Ta méthode finalement fais strtok qui je vais m'efforcer de le rappeler est déconseillé pour les gens qui débutent en C...

  11. #11
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par fearyourself
    Tant qu'à faire des différence entre valeurs de pointeurs, j'ai une préférence pour passer en (void *)
    Ben non.

    Comme une différence de pointeurs renvoi le nombre d'éléments (et non le nombre de bytes), un void* ne permet pas de faire de calcul (du moins en C standard). Il faut donc bien laisser le type. Une fois de plus, le cast est signe d'une bêtise en cours...

    Nota : le type retourné est ptrdiff_t. Il est signé.
    Pas de Wi-Fi à la maison : CPL

  12. #12
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par fearyourself
    PS: Ta méthode finalement fais strtok qui je vais m'efforcer de le rappeler est déconseillé pour les gens qui débutent en C...
    C'est pas une question de débuter ou pas. Le principal problème de strtok() est qu'il n'est pas réentrant. C'est tout. Ca peut être génant ou non. La fonction de Dazumba (qui ressemble à strtok_r()) a au moins l'avantage d'être réentrante.
    Pas de Wi-Fi à la maison : CPL

  13. #13
    Membre à l'essai
    Inscrit en
    Novembre 2003
    Messages
    34
    Détails du profil
    Informations forums :
    Inscription : Novembre 2003
    Messages : 34
    Points : 21
    Points
    21
    Par défaut
    Le problème aussi était que mon fichier d'entrée est très très gros !
    Il contient des dizaines et des dizaines de millier de lignes de plus de 1000 caractères.

    Donc si je compare chaque char avec le séparateur j'ai pas fini !

    Mais j'ai trouvé une solution, certes pas belle du tout mais bon ça marche

    C'est avant de commencer à lire mon fichier je remplace tous les ";;" par des "; ;". Comme ça il me trouve un token entre chaque ';'.

    Aussi le problème étant que il n'y a pas de fonction standard pour faire un substring, donc j'utilise la fonction sed system.

    system("sed 's/;;/; ;/g' input_file.tmp > input_file.tmp2");
    system("sed 's/;;/; ;/g' input_file.tmp2 > input_file.tmp");
    system("rm input_file.tmp2");

    Apres je test si le token vaut " " alors c'est qu'il est NULL !

    Et voilà
    Je sais c'est pas beau du tout

  14. #14
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Donc si je compare chaque char avec le séparateur j'ai pas fini !
    Tu crois vraiment que sed ne fait pas ça... Du coup tu fais trois passes dans ton fichier...
    Jc

  15. #15
    Membre à l'essai
    Inscrit en
    Novembre 2003
    Messages
    34
    Détails du profil
    Informations forums :
    Inscription : Novembre 2003
    Messages : 34
    Points : 21
    Points
    21
    Par défaut
    oui mais le truc c'est que la différence de temps d'exécution entre le sed et mon while/if est énorme !

    Le sed est beaucoup plus rapide il y a pas photo !

  16. #16
    Membre expérimenté
    Inscrit en
    Décembre 2004
    Messages
    1 478
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 478
    Points : 1 664
    Points
    1 664
    Par défaut
    Le sed est beaucoup plus rapide il y a pas photo !
    Tant mieux - mais cela signifie que ton programme est plus que douteux. Le facteur limitant pour un algorithme aussi simple devrait etre la vitesse du disque dur. Remarque aussi que le C n'est surement adapte a ton probleme (i.e. tu utilises un marteau pilon pour ecraser une mouche). Un bon script shell, a coup de awk, sed, peut donner des resultats plus rapidement.

Discussions similaires

  1. [Java 5] Réflexion sur les énumérations type-safe
    Par rozwel dans le forum Langage
    Réponses: 5
    Dernier message: 04/12/2004, 21h34
  2. Critères sur champ de type date
    Par blasco dans le forum Access
    Réponses: 2
    Dernier message: 29/10/2004, 10h48
  3. Fonction LEFT sur champ de type "text" : méthodes
    Par MatthieuQ dans le forum Langage SQL
    Réponses: 4
    Dernier message: 08/06/2004, 12h15
  4. Réponses: 3
    Dernier message: 17/05/2004, 18h28
  5. [Debutant][Tableau] Tableau indexé sur chaine de caractères
    Par SamRay1024 dans le forum Collection et Stream
    Réponses: 3
    Dernier message: 07/05/2004, 12h14

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