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 :

Récupérer une sous chaine de caractère


Sujet :

C

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Août 2002
    Messages
    168
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 168
    Points : 123
    Points
    123
    Par défaut Récupérer une sous chaine de caractère
    Bonjour,
    Voilà, je voudrais avoir vos avis sur la fonction que j'ai écrite. Elle permet de ressortir un bout de chaine compris entre 2 "#". Je débute en C donc je voudrais savoir quelle chose éviter, etc...

    Merci

    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    char *get_label(char * cmd)
    {
        char *label;
        char c = cmd[0];
        int pos_1 = 0;
        int pos_2 = 0;
        int i = 0;
        int cpt = 0;
        int len = strlen(cmd);
     
        /* il existe au moins un # dans la cmd */
        if (strstr(cmd, "#") != NULL)
        {
            /*printf("parcours de la chaine : %s de taille : %d\n", cmd, len);*/
     
            /* parcours de la chaine cmd */
            while (i < len)
            {
                /* si le caractère courant est un # est que c'est le 1er */
                if (c == '#' && cpt == 0)
                {
                    pos_1 = i;
                    cpt++;
                } else if (c == '#' && cpt == 1) /* un 2eme # est trouvé */
                {
                    pos_2 = i;
                    cpt++;
                }
     
                c = cmd[i];
                i++;
            }
     
            /* récupération de la chaine contenu entre les 2 positions */
            label = (char *)realloc(label, pos_2-pos_1-3);
            if (label != NULL)
            {
                cpt = 0; 
                for (i = pos_1+1; i < pos_2-2; i++)
                {
                    label[cpt] = cmd[i];
                    cpt++;
                }
            }
     
            /* debug
            printf("pos_1 : %d\n", pos_1);
            printf("pos_2 : %d\n", pos_2);
            printf("nb de # : %d\n", cpt);
            printf("label :'%s'\n", label); 
            printf("-->fin de get_label\n");*/
        }
        return label;
    }

  2. #2
    Membre Expert

    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Juin 2003
    Messages
    4 506
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2003
    Messages : 4 506
    Points : 5 724
    Points
    5 724
    Par défaut
    Il faut initialiser au départ label à NULL car le realloc ne fonctionne que si tu lui passes en premier paramètre un pointeur récupére par un précédent malloc ou alors si le pointeur vaut NULL.

    Ca évite aussi de renvoyer un pointeur dans les choux si ta première condition n'est pas réalisée.
    " Dis ce que tu veux qui insulte mon honneur car mon silence sera la réponse au mesquin.
    Je ne manque pas de réponse mais : il ne convient pas aux lions de répondre aux chiens ! " [Ash-Shafi'i ]

  3. #3
    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 Alexandre`
    Bonjour,
    Voilà, je voudrais avoir vos avis sur la fonction que j'ai écrite. Elle permet de ressortir un bout de chaine compris entre 2 "#". Je débute en C donc je voudrais savoir quelle chose éviter, etc...
    Etant donné que tu veux une sous-chaine, c'est à dire plus petite (ou égale) à la chaine originale, tu ne risques rien en allouant dès le départ une chaine de la même taille (strdup() sufit pour ça : non standard, mais POSIX.1, donc très portable).

    Ca évitera des realloc() lourdingues et compliqués à gérer correctement...

    • Il manque les headers
    • Cette fonction devrait admettre une chaine non modifiable :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      char *get_label (char const *cmd)
    • Dans certains cas d'erreur, label est retourné alors qu'il n'est pas initialisé. Je recommande NULL comme valeur par défaut.
    • Le bon type pour len est size_t.
    • Par conséquend, le bon type pour i, pos_1 et pos_2 est celui de len.
    • strstr() pour une chaine de un caractère c'est un peu overlkill comme ils disent... strchr() suffit. D'autre part, ces 2 fonctions servent aussi à localiser la premier occurrence de la chaine ou du caractère. Dommage de ne pas utiliser cette propriété... et de re-parcourir la chaine... Pas efficace...
    • Le cast du realloc() est inutile.
    • Le realloc est inutile. malloc() suffit. En plus, avec label non initialisé, c'est le drame... avec NULL, ça va, mais autant mettre malloc()...
    • Le calcul de la taille pos_2 - pos_1 - 3 est horriblement faux ! C'est (pos_2 - pos_1) - 1 + 1 :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
       
      0123456789
         #xx#
      5 - 2 = 3
      - 1 = 2 OK
      +1 = 3 pour le zéro final...
    • La copie est tout aussi horriblement fausse. C'est à partir de pos_1 + 1 et on s'arrête en pos_2... Il manque le 0 final...

    Le résultat est douteux :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    int main (void)
    {
       char const *s = "Hello#wild#world";
     
       char *sub = get_label (s);
     
       if (sub != NULL)
       {
          printf ("'%s'\n", sub);
          free (sub), sub = NULL;
       }
       return 0;
    }
    donne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    'il='
     
    Press ENTER to continue.
    Ou alors, j'ai rien compris à la spécification...

    L'algo me parait un peu compliqué. J'aurais fait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    recherche du premier #
    si trouve
     noter la position
     rechercher le 2ème #
     si trouvé
      noter la position.
      déterminer la taille de la sous-chaine selon les positions
      allouer la sous-chaine
      copier la sous-chaine chaine en fonction des positions
      retourner l'adresse de la sous-chaine
    Finalement, c'est à peu près ton code... une fois ton code mis au point :
    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
     
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
     
    char *get_label (char const *cmd)
    {
       char *label = NULL;
       if (cmd != NULL && *cmd != 0)
       {
          /* il existe au moins un # dans la cmd */
          if (strchr (cmd, '#') != NULL)
          {
             /*printf("parcours de la chaine : %s de taille : %d\n", cmd, len); */
             size_t pos_1 = 0;
             size_t pos_2 = 0;
             {
                size_t i = 0;
                int cpt = 0;
                size_t const len = strlen (cmd);
     
                /* parcours de la chaine cmd */
                while (i < len)
                {
                   int c = cmd[i];
                   /* si le caractère courant est un # est que c'est le 1er */
                   if (c == '#' && cpt == 0)
                   {
                      pos_1 = i;
                      cpt++;
                   }
                   else if (c == '#' && cpt == 1) /* un 2eme # est trouvé */
                   {
                      pos_2 = i;
                      cpt++;
                   }
     
                   i++;
                }
             }
             /* récupération de la chaine contenu entre les 2 positions */
             label = malloc (pos_2 - pos_1);
             if (label != NULL)
             {
                size_t cpt = 0;
                size_t i;
                for (i = pos_1 + 1; i < pos_2; i++)
                {
                   label[cpt] = cmd[i];
                   cpt++;
                }
                label[cpt] = 0;
             }
          }
       }
       return label;
    }
     
    int main (void)
    {
       char const *s = "Hello#wild#world";
     
       char *sub = get_label (s);
     
       if (sub != NULL)
       {
          printf ("'%s'\n", sub);
          free (sub), sub = NULL;
       }
       return 0;
    }
    Ca donne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    'wild'
     
    Press ENTER to continue.
    Je propose ça :
    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
     
    #include <string.h>
    #include <stdlib.h>
     
    char *get_label (char const *cmd)
    {
       char *label = NULL;
       if (cmd != NULL && *cmd != 0)
       {
          /* il existe au moins un # dans la cmd */
          char *p1 = strchr (cmd, '#');
          if (p1 != NULL)
          {
             char *p2 = strchr (p1 + 1, '#');
             if (p2 != NULL)
             {
                size_t len = (size_t) (p2 - 1 - p1);
                label = malloc (len + 1);
     
                if (label != NULL)
                {
                   memcpy (label, p1 + 1, len);
                   label[len] = 0;
                }
             }
          }
       }
       return label;
    }
    Pas de Wi-Fi à la maison : CPL

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Août 2002
    Messages
    168
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 168
    Points : 123
    Points
    123
    Par défaut
    Merci pour la précision hegros

    J'ai une petite question par contre Emmanuel. Si j'alloue à label la même taille que la chaine de départ. Or label aura moins de caractères que celle de départ et donc le \0 sera bien avant la fin de la chaine. D'où ma question, ca passe où tout ce qu'il y a après mon \0 ?

    merci

  5. #5
    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 Alexandre`
    J'ai une petite question par contre Emmanuel. Si j'alloue à label la même taille que la chaine de départ. Or label aura moins de caractères que celle de départ et donc le \0 sera bien avant la fin de la chaine. D'où ma question, ca passe où tout ce qu'il y a après mon \0 ?
    Ben rien, c'est pas utilisé, mais ce sera récupéré au prochain free() de ce bloc... (strdup() fait un malloc())
    Pas de Wi-Fi à la maison : CPL

  6. #6
    Membre Expert

    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Juin 2003
    Messages
    4 506
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2003
    Messages : 4 506
    Points : 5 724
    Points
    5 724
    Par défaut
    Citation Envoyé par Alexandre`
    Si j'alloue à label la même taille que la chaine de départ. Or label aura moins de caractères que celle de départ et donc le \0 sera bien avant la fin de la chaine. D'où ma question, ca passe où tout ce qu'il y a après mon \0 ?

    merci
    Ce sera ignoré.La fonction printf par exemple s'arrête au premier \0 rencontré de même que strlen par exemple.

    Donc ca sert un peu à rien.

    Désolé de te hupper la question Emmanuel.

    EDIT : trop rapide pour moi
    " Dis ce que tu veux qui insulte mon honneur car mon silence sera la réponse au mesquin.
    Je ne manque pas de réponse mais : il ne convient pas aux lions de répondre aux chiens ! " [Ash-Shafi'i ]

  7. #7
    Membre régulier
    Profil pro
    Inscrit en
    Août 2002
    Messages
    168
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 168
    Points : 123
    Points
    123
    Par défaut
    Citation Envoyé par Emmanuel Delahaye
    Etant donné que tu veux une sous-chaine, c'est à dire plus petite (ou égale) à la chaine originale, tu ne risques rien en allouant dès le départ une chaine de la même taille (strdup() sufit pour ça : non standard, mais POSIX.1, donc très portable).

    Ca évitera des realloc() lourdingues et compliqués à gérer correctement...
    Je n'utilise plus le realloc, mais le strdup. j'affecte la même chaine à label au départ.

    Citation Envoyé par Emmanuel Delahaye
    Il manque les headers
    C'est tout dans un fichier main.c pour l'instant. C'est juste pour les tests, je séparerais le tout après. il y a deux headers pour l'instant stdio.h et string.h

    Citation Envoyé par Emmanuel Delahaye
    Dans certains cas d'erreur, label est retourné alors qu'il n'est pas initialisé. Je recommande NULL comme valeur par défaut.
    Je lui affecte la même chaine que cmd (grâce à strdup)

    Citation Envoyé par Emmanuel Delahaye
    Par conséquend, le bon type pour i, pos_1 et pos_2 est celui de len.
    Là je pige pas trop, strlen renvoi un size_t, mais pour i, pos_1 et pos_2, j'ai juste besoin d'un entier standard non ?

    Citation Envoyé par Emmanuel Delahaye
    strstr() pour une chaine de un caractère c'est un peu overlkill comme ils disent... strchr() suffit. D'autre part, ces 2 fonctions servent aussi à localiser la premier occurrence de la chaine ou du caractère. Dommage de ne pas utiliser cette propriété... et de re-parcourir la chaine... Pas efficace...
    J'utilise strchr maintenant pour tester l'existance du caractère. Mais il renvoi un char et non sa position. Comment faire ?[/LIST]
    [/QUOTE]

    J'utilise une ligne de ce type (tiré d'un fichier) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [cmd] # rmfiletest # rm test
    Merci de aide !

  8. #8
    Membre régulier
    Profil pro
    Inscrit en
    Août 2002
    Messages
    168
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 168
    Points : 123
    Points
    123
    Par défaut
    Ok, je vais essayer de revoir mon algo. Je le remettrais ici en essayant de suivre vos remarques.

    EDIT :

    Merci pour le code, je vais le modifier, j'ai trouvé comment récupérer la position via strchr.

  9. #9
    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 Alexandre`
    Je n'utilise plus le realloc, mais le strdup. j'affecte la même chaine à label au départ.
    Même si ma chaine est NULL ou vide ?
    il y a deux headers pour l'instant stdio.h et string.h
    <stdio.h> ne sert pas et il manque <stdlib.h> pour realloc().
    Là je pige pas trop, strlen renvoi un size_t, mais pour i, pos_1 et pos_2, j'ai juste besoin d'un entier standard non ?
    Oui, de même type, Histoire d'être cohérent...

    http://emmanuel-delahaye.developpez....tes.htm#size_t
    J'utilise strchr maintenant pour tester l'existance du caractère. Mais il renvoi un char et non sa position. Comment faire ?
    Il renvoi une adresse... Il faut utiliser les propriétés de l'arithmétique des pointeurs du C : Une différence d'adresses du même tableau donne un nombre d'éléments.
    J'utilise une ligne de ce type (tiré d'un fichier) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [cmd] # rmfiletest # rm test
    Ah bon ? Si tu changes les specs sans prévenir on pas pas être copains...
    Elle permet de ressortir un bout de chaine compris entre 2 "#"
    Alors c'est bien strstr() qu'il faut utiliser mais une fois avec "# " et une fois avec " #"...
    Pas de Wi-Fi à la maison : CPL

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Août 2002
    Messages
    168
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 168
    Points : 123
    Points
    123
    Par défaut
    hum... désolé pour mon manque de clareté dans la formulation du problème... je pensais être clair.

    J'ai testé ta solution ca fonctionne et c'est très simple et logique...

    J'avais réécris en récupérant la position du 1er # hors de la boucle. Mais il reste toujours une boucle alors que celle-ci peut être évitée.

    Bon j'ai appris quelques trucs à éviter thx.

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

Discussions similaires

  1. récupérer une sous chaine de %%D de mon For
    Par touns390 dans le forum Scripts/Batch
    Réponses: 1
    Dernier message: 01/07/2008, 17h14
  2. Extraction d'une sous-chaine de caractère
    Par ninsekh dans le forum Shell et commandes GNU
    Réponses: 6
    Dernier message: 22/04/2008, 08h13
  3. Récupérer une Sous Chaine
    Par ZIED dans le forum Requêtes et SQL.
    Réponses: 1
    Dernier message: 28/11/2007, 10h50
  4. position d'une sous-chaine de caractère
    Par trax44 dans le forum Shell et commandes GNU
    Réponses: 2
    Dernier message: 01/02/2007, 21h14
  5. Réponses: 4
    Dernier message: 07/12/2006, 11h01

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