Forum Programmation.c sscanf et expressions régulières

Posté par  .
Étiquettes : aucune
0
25
oct.
2004
bonjour,

J'ai un petit souci avec la fonction sscanf...
J'ai un fichier qui peut contenir 2 ou 3 champs toujours séparés par des tabulations.
Je lis chaque ligne avec fgets puis met chaque valeurs dans une variable grâce à sscanf.
Le problème c'est que mon 2eme champ peut contenir un espace...
dans ce cas sscanf prend seulement la première partie du champ2
comment faire pour que sscanf prenne cet espace ??

Voici la partie du code qui pose problème :

while (!feof(fp_file)) {
fgets (buffer, MAX_CHAR_BY_LINE, fp_file);
sscanf(buffer, "%30s%*[^ ]", champ1);
sscanf(buffer, " %*[^\t] %30s", champ2);
sscanf(buffer, " %*[^\t] %*[^\t] %30s[\n]%*[^\n]", champ3);
}

Merci par avance...
  • # strtok

    Posté par  . Évalué à 3.

    salut

    essaies plutot strtok c'est fait pour ca (dans string.h)
    • [^] # Re: strtok

      Posté par  . Évalué à 2.

      NOOON !
      Ne jamais utiliser strtok !
      strtok utilise une variable globale, il modifie la chaîne en entrée, et il n'est pas réentrant !
      Si tu veux absolument utiliser une fonction du genre, il y a strtok_r ...

      Mais bon sscanf fonctionne, il faut juste bien récupérer la valeur de retour pour être sûr que tout c'est bien passé sinon bonjour les plantages.

      Une solution serait donc de récupérer tous les champs, et comme le deuxième contient un espace, le récupérer en DEUX champs, puis de les concaténer ... (strcat ou plutôt strncat).
  • # strchr

    Posté par  . Évalué à 2.

    Tu peux utiliser 'strchr' qui permet de chercher un caractère dans une chaîne de caratères.

    L'idée est de rechercher le séparateur de champ et de remplacer celui de la fin du champ par '\0' pour permettre d'utiliser strcpy qui copiera tout avec les espaces.

    Dans l'exemple ci-dessous tous les champs peuvent contenir des espaces, on peut faire un peu plus simple si seul le champ2 peut contenir des espaces.


    char *sp_debut, *sp_fin;

    while (fgets(buffer, MAX_CHAR_BY_LINE, fp_file) != NULL) {
    sp_fin = strchr(buffer, '\t');
    *sp_fin = '\0';
    strcpy(champ1, buffer);
    *sp_fin = '\t';
    sp_debut = sp_fin + 1;
    sp_fin = strchr(sp_debut, '\t');
    *sp_fin = '\0';
    strcpy(champ2, sp_debut);
    strcpy(champ3, sp_fin + 1);
    }
    • [^] # Re: strchr

      Posté par  . Évalué à 2.

      J'ai oublié de remettre le '\t' après le strcpy n° 2 :-}


      [...]
      strcpy(champ2, sp_debut);
      *sp_fin = '\t';
      strcpy(champ3, sp_fin + 1);
      [...]


      Si tu veux un code qui conserve le contenu de 'buffer', sinon tu peux les enlever tous les 2.
      • [^] # Re: strchr

        Posté par  . Évalué à 1.

        oula, j'ai une erreur de segmentation...
        apparement le *sp_fin = '\0'; pose probléme...
        • [^] # Re: strchr

          Posté par  . Évalué à 1.

          J'execute ce programme (test_read.c) comme ceci :

          ./test_read < test_read.txt

          après avoir compilé comme cela :

          gcc -W -Wall -ansi -pedantic test_read.c -o test_read

          avec le fichier suivant (test_read.txt, mélange d'espaces et de tabulations comme tu as spécifié) :

          aaa bbb ccc ddd eee
          111 222 333 444

          et j'obtiens :

          c1 [aaa] c2 [bbb ccc ddd] c3 [eee]
          c1 [111] c2 [222 333] c3 [444]


          --------
          #include <stdio.h>
          #include <string.h>

          #define MAX_CHAR_BY_LINE 1024
          #define MAX_STR 1024

          int main()
          {
          char champ1[MAX_STR], champ2[MAX_STR], champ3[MAX_STR];
          char *sp_debut, *sp_fin;
          char buffer[MAX_CHAR_BY_LINE];

          while (fgets(buffer, MAX_CHAR_BY_LINE, stdin) != NULL) {
          sp_fin = strchr(buffer, '\t');
          *sp_fin = '\0';
          strcpy(champ1, buffer);
          *sp_fin = '\t';
          sp_debut = sp_fin + 1;
          sp_fin = strchr(sp_debut, '\t');
          *sp_fin = '\0';
          strcpy(champ2, sp_debut);
          *sp_fin = '\t';
          sp_debut = sp_fin + 1;
          sp_fin = strchr(sp_debut, '\n');
          *sp_fin = '\0';
          strcpy(champ3, sp_debut);
          *sp_fin = '\n';
          printf("c1 [%s] c2 [%s] c3 [%s]\n", champ1, champ2, champ3);
          }

          return 0;
          }
          --------
        • [^] # Re: strchr

          Posté par  . Évalué à 1.

          Ah oui, si jamais la ligne n'est pas bien formatée (pas deux '\t' dans la ligne), le programme va planter car je ne teste pas la valeur de retour de strchr.

          Pour le faire vraiment propre, il faut rajouter cela après chaque strchr :


          sp_fin = strchr(...);
          if (sp_fin == NULL) {
          fprintf(stderr, "Error: bad format\n");
          exit(1);
          }


          Ou alors tu veux sauter ces lignes là :


          sp_fin = strchr(...);
          if (sp_fin == NULL) {
          continue;
          }
          • [^] # Re: strchr

            Posté par  . Évalué à 1.

            Merci !

            ça marche niquel ! c'est EXACTEMENT ce qu'il me faut....

            while (fgets(buffer, MAX_CHAR_BY_LINE, fp_XF86Config) != NULL) {
            sp_fin = strchr(buffer, '\t');
            if (sp_fin == NULL) {
            continue;
            }
            *sp_fin = '\0';
            strcpy(champ1, buffer);
            *sp_fin = '\t';
            sp_debut = sp_fin + 1;
            sp_fin = strchr(sp_debut, '\t');
            if (sp_fin == NULL) {
            continue;
            }
            *sp_fin = '\0';
            strcpy(champ2, sp_debut);
            *sp_fin = '\t';
            sp_debut = sp_fin + 1;
            sp_fin = strchr(sp_debut, '\n');
            if (sp_fin == NULL) {
            continue;
            }
            *sp_fin = '\0';
            strcpy(champ3, sp_debut);
            *sp_fin = '\n';
            printf("c1 [%s] c2 [%s] c3 [%s]\n", champ1, champ2, champ3);
            }

            en effet il y avait des commentaires en debut de fichier ;)

            cependant je ne suis pas sur de comprendre tout le code....
            sp_fin = strchr(buffer, '\t');
            -> on recherche une tabulation dans le fichier....
            if (sp_fin == NULL) {
            continue;
            }
            -> s'il n'y en à pas, on zappe
            *sp_fin = '\0';
            -> on remplace la tabulation par une fin de chaine (vu que c'est un pointeur, ça marche)...
            strcpy(champ1, buffer);
            -> ...pour faire croire à la fin du buffer, afin que strcpy la copie dans chaine1
            *sp_fin = '\t';
            -> on (re)remplace par une tabullation....
            sp_debut = sp_fin + 1;
            -> on saute par dessus la tabulation, et on recommence

            c'est bien ça ???
            en tout cas merci, ca me parrait "accrobatique" mais sa marche ! je pense que c'est quand même moins hasardeux que sscanf...

            Merci encore...
            • [^] # Re: strchr

              Posté par  . Évalué à 1.

              Pour le dire d'une autre façon (un peu plus technique), mais tu sembles avoir tout compris :

              sp_fin = strchr(buffer, '\t');

              on récupère l'adresse de la prochaine tabulation (depuis 'buffer' qui contient l'adresse du début de la chaîne de caractères) dans un pointeur

              if (sp_fin == NULL) {
              continue;
              }

              si le pointeur est NULL, pas de tabulation trouvée, on saute (= on prend la prochaine ligne avec le 'fgets')

              *sp_fin = '\0';

              ça veut dire exactement cela : on met le caractère '\0' à l'adresse pointée par 'sp_fin' (qui doit pointer vers un '\t')

              strcpy(champ1, buffer);

              la fonction 'strcpy' s'arrête au caractère de fin de chaîne de caractère '\0' que nous avons introduit artificiellement en prenant soin de conserver son adresse pour pouvoir revenir en arrière et remettre le '\t' au bon endroit par la suite

              sp_debut = sp_fin + 1;

              on avance d'une 'case' dans la chaîne de caractère (d'où l'intérêt d'utiliser des pointeurs typés et non pas des 'void *' sinon la machine saurait pas la taille de '+ 1' qui n'est pas la même pour tous les types)

              pour conclure, je dirais que ce n'est pas "accrobatique" mais juste qu'il n'y a pas de fonction toute faite pour cela et qu'il faut décrire vraiment ce que l'on fait, c'est ça le C :o)

              si tu veux que ton travail soit réutilisable, fais toi une fonction qui copie le champ suivant à partir d'une adresse et d'un délimiteur, par exemple :

              char *cpy_next_field(char *field_buffer, char *string, char delim)
              {
              char *sp;

              if ((sp = strchr(string, delim)) == NULL) {
              return NULL;
              }
              *sp = '\0';
              strcpy(field_buffer, string);
              *sp = delim;
              return sp + 1;
              }

              et tu testes la valeur de retour qui est soit NULL si rien n'est trouvé, soit l'adresse du début du champ suivant

              on peut donc maintenant écrire ton programme comme cela :

              while (fgets(buffer, MAX_CHAR_BY_LINE, fp_XF86Config) != NULL) {
              sp = cpy_next_field(champ1, buffer, '\t');
              if (sp == NULL) {
              continue;
              }
              sp = cpy_next_field(champ2, sp, '\t');
              if (sp == NULL) {
              continue;
              }
              sp = cpy_next_field(champ3, sp, '\n');
              if (sp == NULL) {
              continue;
              }
              printf("c1 [%s] c2 [%s] c3 [%s]\n", champ1, champ2, champ3);
              }

              ce qui est quand même plus clair... et si demain tu veux utiliser un autre délimiteur, pas de problème :o)
              (la petite subtilité que tu as dû voir est que le dernier champ est délimité par un retour à la ligne '\n')
              • [^] # Re: strchr

                Posté par  . Évalué à 1.

                Ok merci à tous pour vos réponces ;)
                A+

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.