Faire un don ! | | style | statistiques | contactez-nous | plan | lettre d'information

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

Posté par smorico (page perso, ) le 25 octobre 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...

> Lire le message (10 commentaires, moyenne: 1,5).  

Vous avez demandé le commentaire #489185.

strchr

Posté par asailor () le 25/10/2004 à 11:06. (lien). É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 asailor () le 25/10/2004 à 11:09. (lien). É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 smorico (page perso, ) le 25/10/2004 à 16:53. (lien). Évalué à 1.

      oula, j'ai une erreur de segmentation...
      apparement le *sp_fin = '\0'; pose probléme...

      • [^]Re: strchr

        Posté par asailor () le 26/10/2004 à 08:25. (lien). É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 asailor () le 26/10/2004 à 14:36. (lien). É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 smorico (page perso, ) le 26/10/2004 à 19:57. (lien). É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 asailor () le 27/10/2004 à 12:56. (lien). É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 smorico (page perso, ) le 28/10/2004 à 21:19. (lien). Évalué à 1.

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