1. #1
    Futur Membre du Club
    Profil pro
    Étudiant
    Inscrit en
    octobre 2013
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Bâtiment

    Informations forums :
    Inscription : octobre 2013
    Messages : 4
    Points : 5
    Points
    5

    Par défaut Obtenir informations à partir d'un string

    Bonjour à tous,

    Je suis débutant en C, et je suis venu chercher un peu d'aide.

    Je souhaite écrire une procédure qui permet d'obtenir à partir d'un string en entrée, diverses informations sur le string en question : tout d'abord pouvoir reconnaître les différents mots contenu dans le string, les sauvegarder dans un tableau (tableauDeMots), ainsi que la position du début de mot (PositionDansChaine). Le programme que j'ai écrit ci-dessous compile bien mais j'obtiens un segmentation fault. J'ai beaucoup de mal avec les pointeurs pour l'instant, et la je fais appel à des pointeurs de pointeurs, autant dire que je m'y perd très facilement quant à leur utilisation. Un petit coup de pouce de votre part pour y voir plus clair ne serait pas de refus. Je ne précise pas le code de certaines fonctions que j'utilise dans ma procédure car je suis certains de leur bon fonctionnement.

    Je vous remercie d'avance,


    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
    int compteurDeMots (char* chaine){
      int compteur=1;
     
      for (int i=1;i<strlen(chaine);i++){
        if (estSeparateur(chaine[i])){
          compteur+=1;
        }
      }
    return compteur;
    }
     
    void CORR_decomposer (char* chaine, int compteur, char*** tableauDeMots ,int* PositionDansChaine[]){
    /* On rentre une chaine, et le nombre de mot dans la chaine, on ressort un tableau de chaine ainsi
    que la position du début de chaque mot dans la chaine*/
       int i,j;
       char* res;
       **tableauDeMots=malloc(sizeof(int) * compteur);
       *PositionDansChaine=malloc(sizeof(int)*compteur);
       res=NULL;
       j=0; /*marque le début d'un mot*/
     
       for (i=1;i<strlen(chaine);i++){
         /* On commence le compteur à 1, le premier caractère ne pouvant être un séparateur*/
         if (estSeparateur(chaine[i])){
           res=sous_string(chaine,j,i-1);/*on selectionne le mot que l'on a trouvé dans la chaine, i-1 pour ne pas prendre
           en compte le séparateur*/
           *tableauDeMots[j]=res;
           PositionDansChaine[j]=&i;
           j=i+1;
         }
       }
    }
     
     
     
    int main() {
      char** tabDeMots;
      int nbreDeMots;
      //int* positions;
      char* String_a_decomposer = "String a decomposer";
      int nbreMot = compteurDeMots(String_a_decomposer);
      int *positions;
      CORR_decomposer(String_a_decomposer,nbreMot,&tabDeMots,&positions);
      int bool1=strcmp(tabDeMots[1],"a");
      int bool2=(nbreDeMots==3);
      int bool3=(positions[1]==7);
      printf("%d",bool1);
      return 0;
    }

  2. #2
    Membre expert
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    décembre 2015
    Messages
    750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : décembre 2015
    Messages : 750
    Points : 3 748
    Points
    3 748

    Par défaut

    Bonjour,

    avant d'utiliser un pointeur, il faut lui associer une adresse, ensuite on pourra le déréférencer pour accéder à la mémoire qu'indique le pointeur.
    Ligne 17, **tableauDeMots=malloc(sizeof(int) * compteur); va écrire à l'adresse contenue dans l'adresse indiquée par le pointeur tableauDeMots du main(), *tableauDeMots est le contenu du tableauDeMots qui est totalement aleatoire. C'est mal parti de déréférencer cela. La taille allouée est curieuse, c'est quoi ce sizeof(int)?
    Le reste du code n'est pas mieux.

    Penses à bien nommer tes variables et à faire des dessins pour mieux comprendre les pointeurs. La fonction pourrait commencer par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void CORR_decomposer( char* chaine, int compteur, char*** adrPtrMots , int** adrPosDansChaines ) {
       *adrPtrMots = malloc( sizeof(char**) * compteur ); // allouer 'compteur' * pointeurs
       char** ptrMots = *adrPtrMots;                      // utiliser un nom avec un niveau de pointeur de moins
       for ( int n = 0 ; n < compteur ; ++n )
          ptrMots[n] = NULL;  // pour le moment les pointeurs restent à zéros, ils seront allouées à la bonne longueur de chaine en fonction des longueurs des mots trouvés

  3. #3
    Membre expert
    Inscrit en
    mars 2005
    Messages
    1 301
    Détails du profil
    Informations forums :
    Inscription : mars 2005
    Messages : 1 301
    Points : 3 784
    Points
    3 784

    Par défaut

    Attention : la notion de mot au sein d'une chaîne de caractère dépend de son encodage. Vu ton niveau, on va donc supposer que tu ne veux traiter que des chaînes composées des 127 premiers caractères de la table ASCII dans la locale C par défaut. La solution serait bien différente et plus complexe si tu devais prendre en charge des chaînes comportant des caractères multi-byte (dont la représentation occupe plus d'un byte / char), voire des encodages à taille de caractère variable (variable-length encoding - UTF-8 par exemple - dont la représentation peut occuper un ou plusieurs byte en fonction des caractères).

    Bref. Cela étant précisé et constatant le fait que tu es encore mal à l'aise avec la gestion d'adresses mémoire, mon conseil serait de procéder par étapes. Scinde le problème en autant de sous-problèmes que possible et assure-toi que chaque brique de base de ton programme fonctionne avant de passer à la couche supérieure. Tu dois comprendre exactement ce que tu fais.

    Oublie la gestion dynamique de mémoire, les indirections multiples (double, triple pointeurs..) et le stockage du résultat pour l'instant : peux-tu déjà coder une fonction qui prend une chaîne de caractère et paramètre et en affiche chaque mot à la volée, sur une ligne séparée ? Teste ce module avec la chaîne suivante, par exemple (extraite de cppreference.com) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const char *test = "A null-terminated byte string (NTBS) is a sequence of nonzero bytes followed by a byte with value zero (the terminating null character). Each byte in a byte string encodes one character of some character set.";
    N'hésite pas à utiliser un débugger pas à pas et / ou à afficher l'état de ton programme (le contenu des variables) régulièrement afin de contrôler le comportement obtenu.

    Une dernière chose : je ne sais pas ce que fait estSeparateur ni si l'utiliser est absolument requis, mais tu as isspace dans ctype.h qui devrait faire sensiblement (et mieux) la même chose. Alternativement, on utilise en général strspn et strcspn pour implémenter ton énoncé.

  4. #4
    Futur Membre du Club
    Profil pro
    Étudiant
    Inscrit en
    octobre 2013
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Bâtiment

    Informations forums :
    Inscription : octobre 2013
    Messages : 4
    Points : 5
    Points
    5

    Par défaut

    Merci a vous deux de m'avoir répondu,

    Tout d'abord Dalfab grâce à vos indications j'ai déjà commencé à mieux comprendre les pointeurs, mais je n'arrive toujours pas au résultat escompté

    j'ai écrit un bout de code mais je sais déjà qu'il ne marchera pas et je n'arrive pas à trouver de solutions (j'ai modifié un peu le début de votre proposition parce que j'arrivais pas à faire autrement :/)
    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
    void CORR_decomposer( char* chaine, int compteur, char*** adrPtrMots , int** adrPosDansChaine ) {
       *adrPtrMots = malloc( sizeof(char**) * compteur ); // allouer 'compteur' * pointeurs
       *adrPosDansChaine=malloc(sizeof(int*) * compteur);
       char** ptrMots = *adrPtrMots;
       int* posDansChaine= *adrPosDansChaine;// utiliser un nom avec un niveau de pointeur de moins
       int j=0; /*marque le début d'un mot*/
       for ( int n = 0 ; n < compteur ; ++n ){
           ptrMots[n] = NULL;  // pour le moment les pointeurs restent à zéros, ils seront allouées à la bonne longueur de chaine en fonction des longueurs des mots trouvés
           adrPosDansChaine[n]=NULL;
           for (int i=1;i<=strlen(chaine);i++){
             /* On commence le compteur à 1, le premier caractère ne pouvant être un séparateur*/
             if (isspace(chaine[i]) || i==strlen(chaine)){
               char* s=sous_string(chaine,j,i-1);
               int pos=i;
               ptrMots[n]=s;
               adrPosDansChaine[n]=pos;
               j=i+1;
             }
           }
       }
    }
    En effet j'arrive à voir que les lignes 15 et 16 ne donneront absolument pas ce que je veux :'(

    Matt_Houston, en effet isspace me simplifie la vie, mais ne détectera pas par exemple que "j'ai" est en fait composé de 2 mots à cause de l'apostrophe ,

    voici mon code assez simple pour estSeparateur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int estSeparateur(char c){
      int a = isspace(c);
      return ((a!=0) || (c=='-') || (c==27));
       }
    J'ai fait comme vous me conseilliez de faire, isoler chaque mot d'un string, j'y arrive en prenant en compte les "." et les parenthèses. Ce n'est pas très grave pour l'utilisation que je veux en faire. Voici mon code :
    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
    void decomposer (const char* chaine){
      char* string_A_Afficher;
      int j=0;
      for (int i=1;i<=strlen(chaine);i++){
        /* On commence le compteur à 1, le premier caractère ne pouvant être un séparateur*/
        if (isspace(chaine[i]) || i==strlen(chaine)){
          //printf("x");
          char* string_A_Afficher=sous_string(chaine,j,i-1);
          printf("%s \n",string_A_Afficher);
          j=i+1;
        }
      }
    }
     
    int main(){
      const char *test = "A null-terminated byte string (NTBS) is a sequence of nonzero bytes followed by a byte with value zero (the terminating null character). Each byte in a byte string encodes one character of some character set.";
      decomposer(test);
      return 0;
    }
    Maintenant, reste le plus difficile à mes yeux, stocker les informations qui me sont nécessaires

  5. #5
    Membre expert
    Inscrit en
    mars 2005
    Messages
    1 301
    Détails du profil
    Informations forums :
    Inscription : mars 2005
    Messages : 1 301
    Points : 3 784
    Points
    3 784

    Par défaut

    Citation Envoyé par spypa Voir le message
    Matt_Houston, en effet isspace me simplifie la vie, mais ne détectera pas par exemple que "j'ai" est en fait composé de 2 mots à cause de l'apostrophe ,
    Tu peux compléter le filtrage avec ispunct (clic ! jette un œil au tableau en fin de page). D'une manière générale, utilise autant que possible les fonctions de ta bibliothèque standard dès que tu en as la possibilité. Le programme n'en sera que plus rapide à développer, moins buggé, plus performant et plus future-proof / forward compatible.


    Citation Envoyé par spypa Voir le message
    J'ai fait comme vous me conseilliez de faire, isoler chaque mot d'un string, j'y arrive en prenant en compte les "." et les parenthèses. Ce n'est pas très grave pour l'utilisation que je veux en faire. Voici mon code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void decomposer (const char* chaine){
      char* string_A_Afficher;
      int j=0;
      for (int i=1;i<=strlen(chaine);i++){
        /* On commence le compteur à 1, le premier caractère ne pouvant être un séparateur*/
        if (isspace(chaine[i]) || i==strlen(chaine)){
          //printf("x");
          char* string_A_Afficher=sous_string(chaine,j,i-1);
          printf("%s \n",string_A_Afficher);
          j=i+1;
        }
      }
    }
    Attention à deux choses :

    • Tu extrais une sous-chaîne d'une chaîne constante (déclarée const char *) pour l'afficher. De trois choses l'une, soit sous_string :
      • modifie directement le contenu de la chaîne d'entrée pour y insérer un '\0' et la déclaration est mauvaise ;
      • alloue dynamiquement un bloc mémoire pour stocker chaque sous-chaîne, mais qui n'est pas libéré après affichage et tu as donc un leak ;
      • utilise un autre buffer déclaré soit dans la portée globale soit en statique pour construire et stocker les sous-chaînes, mais franchement ça m'étonnerait.

      Voici une astuce pour ne faire traiter qu'un morceau de chaîne à *printf, et ainsi laisser extraire une sous-chaîne en laissant la chaîne d'entrée inchangée : utiliser le spécificateur de format "%.*s" qui demande un paramètre supplémentaire de type int (pas size_t !) avant le const char * pour spécifier combien de caractères traiter. Ainsi printf("%.*s", 5, "0123456789"); n'affichera que 01234. Le buffer de chars n'a même pas besoin d'être null-terminated !
    • Un appel à strlen n'est pas gratuit ! Pour une chaîne donnée, pense à stocker son résultat une fois pour toute dans une variable dont tu réutiliseras la valeur : const size_t len = strlen(s); Les performances de ce programme-ci importent peu et l'impact de cette modification sera négligeable, mais c'est un bon réflexe à prendre pour plus tard.



    Citation Envoyé par spypa Voir le message
    Maintenant, reste le plus difficile à mes yeux, stocker les informations qui me sont nécessaires
    Souviens-toi que tu n'es pas forcé de recopier le contenu de la chaîne d'entrée ! Pour chaque mot, tu n'as besoin que de deux informations : l'adresse du mot (sa position dans la chaîne) et un entier représentant sa longueur (puisqu'on ne veut pas forcément cribler de '\0' la chaîne d'entrée..). Cf. astuce mentionnée précédemment. Par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    typedef struct word {
        const char *pos;
        size_t len;
    } word_t;

  6. #6
    Futur Membre du Club
    Profil pro
    Étudiant
    Inscrit en
    octobre 2013
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Bâtiment

    Informations forums :
    Inscription : octobre 2013
    Messages : 4
    Points : 5
    Points
    5

    Par défaut

    Après toute une journée de réflexion et après avoir retourné dans tous les sens la définition de pointeur, j'ai enfin réussi à arriver au résultat que je voulais obtenir ! je n'y serai pas arrivé sans vous deux ! Mille merci ! Voilà quand même mon code inspiré de vos conseils à vous deux, peut-être qu'il servira à autrui, et puis toute critique sur ce que j'ai écrit est bonne à prendre pour m'améliorer.
    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
     
    int compteurDeMots (const char* chaine){
       int compteur=1;
     
       for (int i=1;i<strlen(chaine);i++){
          if (estSeparateur(chaine[i])){
              compteur+=1;
            }
     
          }
          //printf("%d\n",compteur);
        return compteur;
        }
     
    int estSeparateur(char c){
       int a = isspace(c);
       return ((a!=0) || (c=='-') || (c=='\''));
        }
     
    void decomposerLaChaine (const char* chaine,char*** adrPtrMots , int** adrPosDansChaines){
     //  char ** tabDeMots;
       int nbreDeMots= compteurDeMots(chaine);
       *adrPtrMots = malloc( sizeof(char**) * nbreDeMots ); // allouer 'compteur' * pointeurs
       *adrPosDansChaines= malloc( sizeof(int*) * nbreDeMots);
       char** ptrMots = *adrPtrMots;
       int* PosDansChaines= *adrPosDansChaines;// utiliser un nom avec un niveau de pointeur de moins
       char* string_A_Afficher;
       int j=0;
       int compteur=0;
       PosDansChaines[compteur]=0;
       for (int i=1;i<=strlen(chaine)+1;i++){
         /* On commence le compteur à 1, le premier caractère ne pouvant être un séparateur*/
         if (estSeparateur(chaine[i]) || i==strlen(chaine)){
           //printf("x");
           printf("%d\n",i);
           char* string_A_Afficher=sous_string(chaine,j,i-1);
           ptrMots[compteur]=string_A_Afficher;
           printf("x");
           //int test2= i-strlen(string_A_Afficher);
           //printf("%d\n",test2);
           PosDansChaines[compteur+1]=i+1;
           compteur+=1;
           //printf("%s \n",string_A_Afficher);
           j=i+1;
         }
         }
    En ce qui concerne ma fonction sous_string, il s'agit du cas numéro 2, j'alloue un espace mémoire que je ne libère pas ensuite. Pour tout vous dire, vous utilisez un vocabulaire qui m'est encore inconnu pour l'instant, il y a des chose qui m'échappent mais j'y reviendrai dessus le moment voulu .

    Je jetterai un oeil à ispunct, ca peut m'aider. Enfin, pour en venir a la structure que vous construisez, j'y avais pensé, et ca rend le code nettement plus lisible mais je manque de temps et je voulais rester sur mon idée de base sans m'éparpiller.

    Merci encore !

  7. #7
    Membre expert
    Inscrit en
    mars 2005
    Messages
    1 301
    Détails du profil
    Informations forums :
    Inscription : mars 2005
    Messages : 1 301
    Points : 3 784
    Points
    3 784

    Par défaut

    Le principal souci que je constate avec ce programme est qu'il va compter un mot pour chaque caractère séparateur rencontré. La fonction va compter des mots si elle rencontre plusieurs séparateurs à la suite ou même si la chaîne commence par un séparateur.

    Ton algo devrait plutôt ressembler à quelque chose comme :

    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
    nombre_mots := 0
    tant que la fin de chaîne n'est pas rencontrée faire
        mot_lu := faux
        tant que c'est un mot faire
            avancer d'un caractère
            mot_lu := vrai
        fin tant que
     
        si mot_lu alors
            nombre_mots := nombre_mots + 1
        fin si
     
        tant que ce n'est pas un mot faire
            avancer d'un caractère
        fin tant que
    fin tant que

    Citation Envoyé par spypa Voir le message
    Pour tout vous dire, vous utilisez un vocabulaire qui m'est encore inconnu pour l'instant, il y a des chose qui m'échappent mais j'y reviendrai dessus le moment voulu .
    N'hésite pas à demander des détails sur tel ou tel terme qui ne te revient pas, un forum est fait pour échanger.

  8. #8
    Futur Membre du Club
    Profil pro
    Étudiant
    Inscrit en
    octobre 2013
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Bâtiment

    Informations forums :
    Inscription : octobre 2013
    Messages : 4
    Points : 5
    Points
    5

    Par défaut

    Effectivement c'est ce que ce programme fera, la chaine en entrée sera normalisée de telle sorte à n'avoir jamais d'espace au début ou plus d'un espace, c'est pour cela que je l'ai codé de cette manière.

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

Discussions similaires

  1. Obtenir type Class à partir d'un String
    Par kobe dans le forum Langage
    Réponses: 6
    Dernier message: 13/03/2007, 09h59
  2. [JDK1.5] Obtenir Annotation à partir d'une String
    Par pitoubicou dans le forum Langage
    Réponses: 9
    Dernier message: 29/03/2006, 18h37
  3. [VB.NET]Comment obtenir un type à partir d'un string?
    Par NicolasJolet dans le forum VB.NET
    Réponses: 2
    Dernier message: 24/02/2006, 15h46
  4. Réponses: 4
    Dernier message: 07/01/2006, 18h36
  5. [DOM] Comment creer un DOM à partir d une String
    Par RolandB dans le forum XML
    Réponses: 3
    Dernier message: 24/04/2005, 18h11

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