Partage
  • Partager sur Facebook
  • Partager sur Twitter

Gérer les entrées en console avec fgets.

Eh oui ... mais non ^^

    9 mai 2006 à 22:52:09

    Bonsoir la dedan. Je commence à savoir manipuler assez bien tout ce qui concerne la console en C vue dans les cours du SdZ.

    Il reste un point obscur à régler : fgets()

    J'ai décidé d'apprendre à bien gérer les entrées utilisateurs en console, car avec scanf, c'est pas très "propre". Oui, car scanf, c'est pour les données formatées, dont ont connait à l'avance la structure/composition.

    Par exemple, un fichier .txt qui contient les highscore de notre jeu. On peut facilement et rapidement le lire avec scanf étant donné qu'on connais exactement sa structure, vu qu'on l'a créé.

    Mais l'utilisateur, lui, même si on lui demande un nombre, il paut taper n'importe quoi ! Et c'est là que scanf() voit ses limites.

    Laissons tomber la gestion des erreur avec scanf, je vais plutôt me tourner vers fgets (si souvent recomendée).

    [HS] Le spitch c'est pour moi, si je suis capable d'expliquer, c'est j'ai compris, et vous pourrez me corriger si j'ai commis une erreur. [/HS]

    Tout d'abord, fgets... On s'en sert dans le cours pour lire dans un fichier. Et c'est plutôt pratique !

    char* fgets(char* chaine, int nombreDeCaracteresALire, FILE* pointeurSurFichier);


    chaine -> La chaine dans laquelle on veut enregistrer la lecture.
    nombre... -> Nombre de caractère maximum à lire. Prévoire une place pour l' \0 de fin de chaîne.
    pointeur... -> Le pointeur qui pointe sur le fichier dans lequel on veut lire.

    Quelque chose qui me trouble: la fonction est de type char* .
    Mais on l'utilise sans retour, a mon avis, elle retourne le pointeur sur le char qu'elle a modifié, et on peut l'utiliser pour verification non ? (pas sûr du tout).

    Pour lire les entrées utilisateur maintenant, regardons un exemple (by moi, c'est peut être mauvais)

    (je me dispense du contexte du programme pour mes codes)

    char nom[10];

    printf("Bonjour, entrez votre nom :");

    fgets( nom, sizeof(nom), stdin );

    printf("\nVous vous appelez: %s\n", nom);


    Bonjour, entrez votre nom: Benji

    Vous vous appelez: Benji


    Bon, ça marche, stdin représentant l'entrée standard. A ce propos j'ai une question, stdin est de type FILE* ? Car dans le prototype ... enfin, je me doute qu'il doit y avoir un truc.

    Mais le problème est qu'il reste des caractères non lus si l'utilisateur en a entré plus que la limite imposée. On peut le voir si l'on effectue un second fgets:

    char nom[10];

    printf("Bonjour, entrez votre nom :");

    fgets( nom, sizeof(nom), stdin );

    printf("\nVous vous appelez: %s\n", nom);

    printf("Entrez un autre nom :");

    fgets( nom, sizeof(nom), stdin );

    printf("\nL'autre nom est : %s\n", nom);


    Bonjour, entrez votre nom: Unnompluslongque10caracteres

    Vous vous appelez: Unnomplus

    Entrez un autre nom :
    L'autre nom est : longque10


    Normal. Il faut donc "vider le cache", vider le buffer clavier dit-on.
    Vider le buffer clavier sur developpez.com

    char nom[10];

    printf("Bonjour, entrez votre nom :");

    fgets( nom, sizeof(nom), stdin );

    printf("\nVous vous appelez: %s\n", nom);

    int c;
    /* Donc on liste les caractères jusqu'a arriver a "Entrée" */
    while( ( c = getchar()) != '\n' && c != EOF );

    printf("Entrez un autre nom :");

    fgets( nom, sizeof(nom), stdin );

    printf("\nL'autre nom est : %s\n", nom);


    La varification de EOF est-elle nécessaire ici ? Car on est pas dans un fichier, il n'y a pas de fin, en tout cas on ne peut pas terminer la saisie autrement que par "Entrée" \n ?

    Mais là, ça donne des truc un peu plus bizarre en console. Si la saisie est moins importante que la limite, je dois réappuyer sur entrée pour passer après le while, le \n serait-il inclus dans la chaîne ?

    A ce moment, comment palier à ça ?

    Tout ça est pour les chaînes pour le moment. Mais comment cela se passe-t-il pour les nombres ? fgets() demande un pointeur sur char, donc seulement une chaîne. Dans ce cas, il faudrait convertir la chaine en (int) ?

    Ou alors pour les nombres, c'est peut être mieux de faire un scanf() avec gestion des erreurs ?

    Tout ça pour dire que cette partie est encore floue dans mon esprit, et je ne trouve pas de documentation assez claire qui m'aide, je trouve des exemples concrets, mais bizarrez ou bien que je n'arrive pas entièrement à comprendre.

    J'ai trouvé particulièrement intéressant sur deceloppez.com, et je pense pourvoir résoudre mon problème de tout à l'heure.

    Au fait, quand la chaine est inférieure à la limite, c'est caractérisé par la présence d'un \n dans la chîne. Il suffit de le remplacer par \0 pour indiquer la fin de la chaine, et on sait d'avance que le buffer est vide. Sinon, le buffer n'est pas vide (mais la chaîne est automatiquement terminée par l'\0), et il faut vider le buffer clavier.

    J'ai notemment besoin de précisions sur le retour de fgets(), qui est utilisé dans l'exemple précédent. Et aussi sur la façon de lire un nombre.

    J'espers que certaines personnes auront le courage de lire mon post, et je les en remercie d'avance.

    Personnellement, je pense avoir éclairci la plupart de mes idées en écrivant ce post, et après réflexion je pense avoir compris la facon d'utiliser fgets() pour une lecture parfaire avec vidage du buffer.

    Enfin, on verra bien !

    Moi.
    • Partager sur Facebook
    • Partager sur Twitter
      9 mai 2006 à 23:14:39

      Citation : benji

      nombre... -> Nombre de caractère maximum à lire. Prévoire une place pour l' \0 de fin de chaîne.


      Non, fgets ajoute le '\0' automatiquement, par exemple si j'ai un tableau de 10 char et que je passe comme deuxième arguement "sizeof <nom_tableau>", fgets lira au maximum 9 caractères et ajoutera le '\0'.

      Citation : benji

      A ce propos j'ai une question, stdin est de type FILE* ?


      Correct, il s'ouvre au début du programme et se ferme à la fin, tout ça automatiquement.

      Citation : benji

      Mais le problème est qu'il reste des caractères non lus si l'utilisateur en a entré plus que la limite imposée. On peut le voir si l'on effectue un second fgets:


      Pas besoin d'un second fgets, il suffit de chercher le caractère de fin de ligne ('\n'), si il n'y est pas ça veut dire que tous les caractères non pas été lu. Tu peux alors choisir de vider le buffer ou de refaire une deuxième lecture pour les récupérer.

      Citation : benji

      La varification de EOF est-elle nécessaire ici ? Car on est pas dans un fichier, il n'y a pas de fin, en tout cas on ne peut pas terminer la saisie autrement que par "Entrée" \n ?


      Non, il peut aussi avoir la valeur EOF (s'obtient en faisant Ctrl+Z sur MS windows ou Ctrl+D sur GNU/Linux).

      Citation : benji

      Si la saisie est moins importante que la limite, je dois réappuyer sur entrée pour passer après le while, le \n serait-il inclus dans la chaîne ?

      A ce moment, comment palier à ça ?


      Vérifier que le '\n' est bien présent dans la chaîne : strchr(), puis test avec la valeur de retour.

      Citation : benji

      Dans ce cas, il faudrait convertir la chaine en (int) ?


      Oui, utilise sscanf ou les fonctions de l'en-tête stdlib (strtol, strtoul ..)

      Citation : benji

      J'ai notemment besoin de précisions sur le retour de fgets(), qui est utilisé dans l'exemple précédent.


      La valeur de retour est un pointeur sur la chaîne saisie, si la valeur EOF est présente alors elle retourne NULL, idem quand il y a une erreur.

      ++
      • Partager sur Facebook
      • Partager sur Twitter
        9 mai 2006 à 23:22:15

        Eh bien ! Tu as répondu très précisément et ce très rapidement !

        Merci beaucoup !

        J'ai compris le principe de la récupération de chaînes, et je coderai une fonction pour le faire pour m'entrainer et voir si j'ai bien tout compris (mais demain car là je dois fermer le PC :( )

        Et puis j'essayrai de le faire aussi pour la saisie de nombres. Je posterai si j'ai des problèmes.

        PS: Je pense que ce post peut être très utile pour les débutants en C comme moi, (presque) tout est plus clair maintenant ;)

        Merci beaucoup à toi Araya !
        • Partager sur Facebook
        • Partager sur Twitter
          10 mai 2006 à 9:54:12

          Citation : Benjitheone


          [HS] Le spitch c'est pour moi, <...> vous pourrez me corriger si j'ai commis une erreur. [/HS]


          speech...

          Citation : Benjitheone


          Tout d'abord, fgets... On s'en sert dans le cours pour lire dans un fichier. Et c'est plutôt pratique !


          As-tu bien compris que ça servait à lire une ligne...

          Citation : Benjitheone


          char* fgets(char* chaine, int nombreDeCaracteresALire, FILE* pointeurSurFichier);



          chaine -> La chaine dans laquelle on veut enregistrer la lecture.
          nombre... -> Nombre de caractère maximum à lire. Prévoire une place pour l' \0 de fin de chaîne.
          pointeur... -> Le pointeur qui pointe sur le fichier dans lequel on veut lire.

          Quelque chose qui me trouble: la fonction est de type char* .


          Non. La fonction retourne une valeur de type pointeur sur char.

          Citation : Benjitheone


          Mais on l'utilise sans retour, a mon avis, elle retourne le pointeur sur le char qu'elle a modifié, et on peut l'utiliser pour verification non ? (pas sûr du tout).


          Ton avis importe peu. Ce qui compte c'est la définition de la fonction. Il suffit de lire la doc...

          http://man.developpez.com/

          Citation : Benjitheone


          Pour lire les entrées utilisateur maintenant, regardons un exemple (by moi, c'est peut être mauvais)

          (je me dispense du contexte du programme pour mes codes)

          char nom[10];

          printf("Bonjour, entrez votre nom :");

          fgets( nom, sizeof(nom), stdin );

          printf("\nVous vous appelez: %s\n", nom);



          Bonjour, entrez votre nom: Benji

          Vous vous appelez: Benji



          Correct. Les () du sizeof sont superflues avec un objet :
          fgets( nom, sizeof nom, stdin );

          Par contre, est-tu bien conscient que si la lecture a été complète, il y a un '\n' au bout de la chaine ?

          Citation : Benjitheone


          Bon, ça marche, stdin représentant l'entrée standard. A ce propos j'ai une question, stdin est de type FILE* ?


          Oui. C'est un des trois flux ouverts automatiquement au démarrage d'un programme C avec stdout et stderr.

          Citation : Benjitheone


          Mais le problème est qu'il reste des caractères non lus si l'utilisateur en a entré plus que la limite imposée. On peut le voir si l'on effectue un second fgets:


          Absolumement. Mais on le sait, car il manque le '\n'. (C'est fait pour)

          Citation : Benjitheone


          char nom[10];

          printf("Bonjour, entrez votre nom :");

          fgets( nom, sizeof(nom), stdin );

          printf("\nVous vous appelez: %s\n", nom);

          printf("Entrez un autre nom :");

          fgets( nom, sizeof(nom), stdin );

          printf("\nL'autre nom est : %s\n", nom);



          Bonjour, entrez votre nom: Unnompluslongque10caracteres

          Vous vous appelez: Unnomplus

          Entrez un autre nom :
          L'autre nom est : longque10



          Normal. Il faut donc "vider le cache", vider le buffer clavier dit-on.


          o_O Ne parle pas de 'cache' ici. Rien à voir. C'est le flux entrant (ici, stdin). Point.

          Citation : Benjitheone


          Vider le buffer clavier sur developpez.com

          char nom[10];

          printf("Bonjour, entrez votre nom :");

          fgets( nom, sizeof(nom), stdin );

          printf("\nVous vous appelez: %s\n", nom);

          int c;
          /* Donc on liste les caractères jusqu'a arriver a "Entrée" */
          while( ( c = getchar()) != '\n' && c != EOF );

          printf("Entrez un autre nom :");

          fgets( nom, sizeof(nom), stdin );

          printf("\nL'autre nom est : %s\n", nom);



          Ce code est incorrect. Il ne faut vider stdin que si on a constaté l'absence de '\n'.

          Citation : Benjitheone


          La vaérification de EOF est-elle nécessaire ici ? Car on est pas dans un fichier, il n'y a pas de fin, en tout cas on ne peut pas terminer la saisie autrement que par "Entrée" \n ?


          Oui, c'est nécessaire, parce que
          • L'utilisateur peut très bien envoyer un signal 'fin de lecture' comme Ctrl-D (Unixoide) ou Ctrl-Z<enter> (DOS/Windows)
          • stdin pourrait très bien être redirigé depuis un fichier (par exemple pour faire une simulation de commandes humaines en test...)
          $ myapp < commandes.txt
          </ul>

          Citation : Benjitheone



          Mais là, ça donne des truc un peu plus bizarre en console. Si la saisie est moins importante que la limite, je dois réappuyer sur entrée pour passer après le while, le \n serait-il inclus dans la chaîne ?

          A ce moment, comment palier à ça ?


          Comme expliqué ci-dessus, ce code est incorrect. Je recommande ceci:

             fgets(line, sizeof line, fp);
             clean(line, fp);

          avec

          void clean (char *s, FILE *fp)
          {
             /* search ... */
             char *p = strchr (s, '\n'); /* <string.h> */
             if (p != NULL)
             {
                /* ... and kill */
                *p = 0;
             }
             else
             {
                /* purge */
                int c;
                while ((c = fgetc(fp)) != '\n' && c != EOF)
                {
                }
             }
          }

          Citation : Benjitheone



          Tout ça est pour les chaînes pour le moment. Mais comment cela se passe-t-il pour les nombres ? fgets() demande un pointeur sur char, donc seulement une chaîne. Dans ce cas, il faudrait convertir la chaine en (int) ?


          Oui.

          Citation : Benjitheone


          Ou alors pour les nombres, c'est peut être mieux de faire un scanf() avec gestion des erreurs ?


          Non. On utilise :
          • strtol()
          • strtoul()
          • strtod()
          • sscanf()

          Citation : Benjitheone


          Tout ça pour dire que cette partie est encore floue dans mon esprit, et je ne trouve pas de documentation assez claire qui m'aide, je trouve des exemples concrets, mais bizarrez ou bien que je n'arrive pas entièrement à comprendre.


          Je recommande ceci. A lire et à relire...
          http://mapage.noos.fr/emdel/notes.htm#saisie
          http://mapage.noos.fr/emdel/notes.htm#fichiers

          Pose des questions si tu ne comprends pas.

          En tout cas, je ne peux que te féliciter pour ta démarche de recherche qui augure d'un avenir prometteur dans ce métier.
          • Partager sur Facebook
          • Partager sur Twitter
          Music only !
            10 mai 2006 à 16:40:17

            Citation : -ed-

            Citation : Benjitheone


            [HS] Le spitch c'est pour moi, <...> vous pourrez me corriger si j'ai commis une erreur. [/HS]


            speech...


            :-° j'ai hésité ! Et j'ai perdu !

            Citation : -ed-


            Citation : Benjitheone


            Tout d'abord, fgets... On s'en sert dans le cours pour lire dans un fichier. Et c'est plutôt pratique !


            As-tu bien compris que ça servait à lire une ligne...


            Bien sûr, je ne l'ai pas précisé ici, mais je l'ai compris.

            Citation : -ed-


            Citation : Benjitheone


            Mais on l'utilise sans retour, a mon avis, elle retourne le pointeur sur le char qu'elle a modifié, et on peut l'utiliser pour verification non ? (pas sûr du tout).


            Ton avis importe peu. Ce qui compte c'est la définition de la fonction. Il suffit de lire la doc...

            http://man.developpez.com/


            Merci pour ce lien, j'avai pas de doc de référence, je piochais tout le temps à droite à gauche, maintenant j'aurai ça en plus !

            Citation : -ed-


            Correct. Les () du sizeof sont superflues avec un objet :

            fgets( nom, sizeof nom, stdin );


            Par contre, est-tu bien conscient que si la lecture a été complète, il y a un '\n' au bout de la chaine ?


            Oui oui, c'est logique. Mais alors, si on demade le nom de quelqu'un, par exemple je rentre 'benji<enter>', dans une chaîne de 10 cases, mon nom sera 'benji\n\0', et à l'écran cela se traduira par un saut de ligne. Le code suivant pourrait-il l'empêcher, et transformer la chaîne 'benji\n\0' en 'benji \0' ?
            char nom[10];

            printf("Bonjour, entrez votre nom :");

            fgets( nom, sizeof nom, stdin );
            clean( nom, stdin); /* Même but que ta fonction = vidder le buffer si le lecture n'est pas complète */

            char *p = strchr(nom, '\n');

            if( p != NULL ) /* Si il y a '\n' dans nom */
                *p = ' ';

            printf("\nVous vous appelez: %s\n", nom);

            Citation : -ed-


            Comme expliqué ci-dessus, ce code est incorrect. Je recommande ceci:


               fgets(line, sizeof line, fp);
               clean(line, fp);


            avec


            void clean (char *s, FILE *fp)
            {
               /* search ... */
               char *p = strchr (s, '\n'); /* <string.h> */
               if (p != NULL)
               {
                  /* ... and kill */
                  *p = 0;
               }
               else
               {
                  /* purge */
                  int c;
                  while ((c = fgetc(fp)) != '\n' && c != EOF)
                  {
                  }
               }
            }



            C'est parfaitement clair maintenant, je comprends. Au départ je me posais des questions sur ceci :

            *p = 0; /* Ton code et ... */
            *p = '\0'; /* de developpez.com */

            Mais j'ai vite trouvé la réponse :
            printf("%d", '\0');

            Cela revient au même, tout comme mettre 'A' et 65.

            Citation : -ed-


            Citation : Benjitheone


            Tout ça est pour les chaînes pour le moment. Mais comment cela se passe-t-il pour les nombres ? fgets() demande un pointeur sur char, donc seulement une chaîne. Dans ce cas, il faudrait convertir la chaine en (int) ?


            Oui.

            Citation : Benjitheone


            Ou alors pour les nombres, c'est peut être mieux de faire un scanf() avec gestion des erreurs ?


            Non. On utilise :

            • strtol()
            • strtoul()
            • strtod()
            • sscanf()



            C'est bien ce que je pensais, c'est clair maintenant. Je ne connais pas encore bien ces fonctions, mais j'ai le temps de les regarder et de les étudier, et le lien que tu m'a donné (ci-dessous) ca m'être utile, merci.

            Citation : -ed-


            Citation : Benjitheone


            Tout ça pour dire que cette partie est encore floue dans mon esprit, et je ne trouve pas de documentation assez claire qui m'aide, je trouve des exemples concrets, mais bizarrez ou bien que je n'arrive pas entièrement à comprendre.


            Je recommande ceci. A lire et à relire...
            http://mapage.noos.fr/emdel/notes.htm#saisie
            http://mapage.noos.fr/emdel/notes.htm#fichiers

            Pose des questions si tu ne comprends pas.

            En tout cas, je ne peux que te féliciter pour ta démarche de recherche qui augure d'un avenir prometteur dans ce métier.


            J'avai déjà feuilleté plusieurs page de http://mapage.noos.fr/emdel/, et je pense que je vais continuer ! Tu as fait un boulot impressionnant, car tous les sujets un peu plus délicats sont plus clairs (tout du moins pour moi).
            Merci pour le compliment. En tout cas c'est impressionnant la quantité de choses que tu connais.
            • Partager sur Facebook
            • Partager sur Twitter
              14 mai 2006 à 20:43:30

              J'ai beaucoup appris en vous lisant ! J'avoue que les explications du site d'E.D. étaient au départ assez asbtraites. Tout me parait clair à présent !
              Merci :)
              • Partager sur Facebook
              • Partager sur Twitter

              Gérer les entrées en console avec fgets.

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown