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

  1. #1
    Membre régulier
    fgets problème utilisation code de la faq
    Bonjour,

    j'ai regardé la faq à la question : Comment lire une ligne de manière sécurisée ?

    J'ai fait un code naïf des plus simple en reprenant le code de la question de la faq mais cela me renvoi rien.
    Je suis sous mingw32-make et la version est c11.

    Voici mon code :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    char * Filename_RecoveryInputUser(void)
    {
            // fgets me renvoi rien on dirait qu'il bug car il ne m'affiche même pas la chaîne.
        char *pFilename = fgets(pFilename, PATH_MAX, stdin);
        printf("%s\n", pFilename); 
        pFilename[strlen(pFilename)-1] = '\0';
        return pFilename;
    }


    De plus comme pFilename est une variable local normalement elle est détruite quand la fonction ce termine.
    Donc faire retourner pFilename même si celui-ci n'est pas null on perd l'adresse retourné non ?

  2. #2
    Expert éminent
    Citation Envoyé par hbx360 Voir le message
    Donc faire retourner pFilename même si celui-ci n'est pas null on perd l'adresse retourné non ?
    Il faut recopier la chaîne de caractères avec la fonction strcpy (<- lien cplusplus.com en anglais)

    Donc soit
    • Soit tu crées 1 chaîne de caractères en local avec malloc, tu fais la recopie, et tu la retournes. Donc c'est la fonction appelante, qui récupére l'adresse retournée avec 1 assignation, et qui a la responsabilité de libérer cette chaîne de caractères.
    • Soit tu passes en paramètres 1 double pointeur char** (<- qui est en réalité l'adresse d'1 chaîne de caractères, &str), tu fais la recopie avec en déréférencent évidement.

  3. #3
    Expert éminent sénior
    Bonjour
    Citation Envoyé par hbx360 Voir le message
    char *pFilename = fgets(pFilename, PATH_MAX, stdin);
    Ca c'est pas bon. fgets(s, n, flux) a pour action de récupèrer "n-1" caractères depuis "flux" et stocker ces caractères dans "s", "s" étant donc prévue pour pouvoir stocker ces "n-1" caractères (+1 pour le '\0'). Donc "s" doit être à minima un tableau de caractères soit dynamique (ie char s[1000]), soit alloué, soit provenant de l'appelant.
    Accessoirement, ensuite fgets() retourne "s" ok, mais ce retour est donc un truc déjà existant au préalable. Elle aurait pu tout aussi bien renvoyer 1 ou n ou n'importe quoi différent de 0. Là tu déclares un pointeur, pointeur qui récupère le retour d'un truc qui le reçoit en paramètre tout en n'existant pas encore. Quelque part ça ne peut pas fonctionner. C'est presque comme si tu te prenais les pieds par en dessous en espérant t'envoler.

    Pour simplifier: la partie droite du "=" est toujours évaluée avant la partie gauche. Donc tu n'as absolument pas le droit d'utiliser dans cette partie droite un élément qui n'existe pas encore.

    Citation Envoyé par hbx360 Voir le message
    Donc faire retourner pFilename même si celui-ci n'est pas null on perd l'adresse retourné non ?
    Hé oui. C'est évidemment une conséquence directe du problème ci-dessus.
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  4. #4
    Modérateur

    En admettant que ton code soit comme ceci :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <stdio.h>
    #include <string.h>
     
    #define PATH_MAX 1000
     
    char * Filename_RecoveryInputUser(void)
    {
            // fgets me renvoi rien on dirait qu'il bug car il ne m'affiche même pas la chaîne.
    	char *pFilename = fgets(pFilename, PATH_MAX, stdin);
    	printf("%s\n", pFilename); 
    	pFilename[strlen(pFilename)-1] = '\0';
    	return pFilename;
    }


    Tu devrais surtout avoir un warning de ton compilateur :
    prog.c: In function 'Filename_RecoveryInputUser':
    prog.c:10:20: warning: 'pFilename' is used uninitialized in this function [-Wuninitialized]
       9  |  char *pFilename = fgets(pFilename, PATH_MAX, stdin);
          |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Tu as bien mis les options qui vont bien à ton compilateur ? (pour gcc et clang : -Wall -Wextra)


    Citation Envoyé par foetus Voir le message
    Il faut recopier la chaîne de caractères avec la fonction strcpy (<- lien cplusplus.com en anglais)

    Donc soit
    • Soit tu crées 1 chaîne de caractères en local avec malloc, tu fais la recopie, et tu la retournes. Donc c'est la fonction appelante, qui récupére l'adresse retournée avec 1 assignation, et qui a la responsabilité de libérer cette chaîne de caractères.
    • Soit tu passes en paramètres 1 double pointeur char** (<- qui est en réalité l'adresse d'1 chaîne de caractères, &str), tu fais la recopie avec en déréférencent évidement.
    Mais pourquoi copier avec strcpy ?

    Si on te passe le buffer, fgets() écrit dedans et c'est bon. Si non, tu crées le buffer, fgets() écrit dedans, tu le renvoies et c'est bon.

  5. #5
    Expert éminent
    Citation Envoyé par Bktero Voir le message
    Mais pourquoi copier avec strcpy ?
    Parce que je n'ai pas compris la question "Donc faire ?"

    Et donc, j'ai répondu de façon générale
    Ensuite la fonction fgets retourne son paramètre (1 truc de la libc qui permet de chaîner les appels), mais le paramètre doit être 1 vrai tableau : dans l'exemple ce n'est pas très clair
    Parce que si tu crées 1 tableau fixe local char file_str[XXX];, tu ne peux pas le retourner. Il faut faire 1 recopie

  6. #6
    Membre régulier
    Le code que je vous ai posté c'est celui de la faq, il faudrait faire les modifications nécessaire je pense .

    Voici le code de la faq :

    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
     
    char * read_stdin(char * buffer, size_t taille) 
    { 
        char * result = fgets(buffer, taille, stdin); 
     
        if (result != NULL) 
        { 
            char * lf = strchr(buffer, '\n'); /* On cherche le caractère '\n'. */ 
     
            if (lf != NULL) /* S'il est présent, ... */ 
                *lf = '\0'; /* ... on le supprime    */ 
            else 
            { 
                /* 
                * Le '\n' n'est pas présent. Ça signifie qu'il reste au moins un 
                * caractère dans stdin. On peut choisir de les ignorer et de vider 
                * stdin ou d'agrandir le buffer si c'est possible (realloc()) et de 
                * rappeler fgets() autant de fois que nécessaire... 
                * 
                * Si on ne fait rien, la prochaine lecture sur stdin se fera sans attente 
                * et récupèrera ce qui n'a pas été lu ... 
                */ 
            } 
        } 
     
        return result; 
    }



    @foetus : ok compris.

    Citation Envoyé par Sve@r Voir le message


    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    char *pFilename = fgets(pFilename, PATH_MAX, stdin);


    Ca c'est pas bon.
    Bin c'est le code de la faq.

    @Bktero : bin je les ai pas mis je vais le faire.

    J'ai une autre question quand je veux vider le buffer en utilisant le code de la faq mais avec fgetc au lieu de getchar, j'ai le problème suivant c'est qu'il faut que je tape 2 fois entrée pour que le programme passe à la suite. Et si je dit à l'utilisateur de taper sur entrée pour continué et bien idem faut que je le fasse 2 fois. J'ai le même problème avec getchar.
    Y aurait-il une possibilité pour ne pas à avoir à taper entrée 2 fois ?


    Voici une vidéo qui montre ce problème :



    Voici mon code ou l'on entre le nom du fichier :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    char pFilename[PATH_MAX]; // Buffer pour récupérer la saisie utilisateur (stdin).
    char * Filename_RecoveryInputUser(void)
    {
    	fgets(pFilename, PATH_MAX, stdin); 
    	pFilename[strlen(pFilename)-1] = '\0';
            Keyboard_CleanStdin();
    	return pFilename;
    }


    Le code pour vider le buffer :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    void Keyboard_CleanStdin(void)
    {
      s32 c;
      do
      {
        // Note : si on met stdin la console attendra toujours que l'utilisateur tape entrée, que ce soit
    		// fgetc ou fgets pour valider.
        c = fgetc(stdin); 
      }while(c != '\n' && c !=EOF);
    }


    Mon autre code (la 2e ligne ou l'on tape entrée pour continuer) :

    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
     
    void Filename_WaitingTxt(void)
    {
    	char pInput[2] = {};
    	int i = 0;
     
    	printf("Attente. Tapez entrée pour continuer.\n");
    	fgets(pInput, 2, stdin);
    	Keyboard_CleanStdin();
     
    	while( pInput[i] != '\n')
    	{
    		printf("Tapez  entrée pour continuer.\n");
    		fgets(pInput, 2, stdin);
    		Keyboard_CleanStdin();
    	}
    }

  7. #7
    Rédacteur/Modérateur

    Citation Envoyé par hbx360 Voir le message
    Bin c'est le code de la faq.
    Y'a un monde entre le code de la FAQ qui prend un buffer en paramètre et l'utilise, puis stocke le retour de fgets dans une autre variable et ton truc qui crée un char* pFilename juste avant son utilisation dans fgets, sans aucune forme d'initialisation, tout en récupérant son retour dans la même variable.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  8. #8
    Membre régulier
    J'ai recopié le code de la faq.
    C'est la seul solution que j'ai trouvé, de déclarer mon tableau en variable global et de le retourner car je met la fonction dans une autre

    Voici le code :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
     
    FILE *pFile = Filename_OpenFile(Filename_RecoveryInputUser(), "r", NULL);.


    ATTENTION : j'ai pas dis que je critiquais la faq je signal un problème je cherche à comprendre je ne suis pas dans une démarche violente ni agressive et je suis dans le forum débutant. Merci de ne pas être agressif !

  9. #9
    Rédacteur/Modérateur

    Tu peux tourner ça dans tous les sens que tu veux, ton code et celui de la FAQ n'ont rien à voir. Et le code de la FAQ est correcte. Le tien, non.
    Quant à utiliser fgets pour retourne un char*, il t'a été expliqué plus haut comment faire.
    fgets prend un buffer. Un buffer c'est pas un pointeur random. C'est de la mémoire qui doit exister, allouée dans la heap via malloc ou sur la stack.
    Utiliser une globale pour ça c'est loin d'une bonne idée ou façon de faire.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  10. #10
    Membre régulier
    Ok.

  11. #11
    Membre éprouvé
    Comme déjà dit, la faq est correcte.

    Une solution simple à ton problème
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    char * Filename_RecoveryInputUser(void) {
    	char *pFilename = fgets(malloc(PATH_MAX),PATH_MAX,stdin);    // en espérant que malloc() se passe bien
    	printf("%s\n", pFilename); 
    	pFilename[strlen(pFilename)-1] = '\0';
    	return pFilename;
    }

  12. #12
    Membre régulier
    Ok pas de soucie, c'est moi qui me suis précipité ; j'ai fait une erreur j'aurai dû mieux relire.

    @edgarjacobs : merci pour ton code.

    Le fait de déclarer
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    char pFilename[PATH_MAX]
    en variable global je sais que c'est le mal mais c'est quand même pratique. Sinon oui j'aurai pu l'envoyé en argument dans la fonction
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
     Filename_RecoveryInputUser
    .

    Merci à tous pour votre aide après pour les "2 fois entrée" je sais pas s'il y a possibilité de régler ça.

  13. #13
    Membre régulier
    Je reviens sur mon code, donc vous me confirmé que ce code est moisie.

    Le main :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    #include "includes.h"
     
    int main(int argc, char const **argv)
    {
        freopen("CON", "w", stdout);
        printf("Saisir le nom du fichier.\n");
        FILE *pFile = Filename_OpenFile(Filename_RecoveryInputUser(), "r", NULL);
        ......... 
     
        return 0;
    }


    Dans filename.c

    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
     
    FILE *Filename_OpenFile(char *pFilename, char *pMode, char *pErrorMsg)
    {
    	FILE *pFile = fopen(pFilename, pMode); // fopen n'admet pas les '\n'.
    	if(pFile == NULL && pErrorMsg == NULL)
    	{
    		printf("Failed to open file.\n");
    	}
    	else if(pFile == NULL)
    	{
    		printf("Failed to open file.\n");
    		printf(pErrorMsg);
    		printf("\n");
    	}
    	return pFile;
    }
     
     
    char pFilename[PATH_MAX]; // Buffer pour récupérer la saisie utilisateur (stdin).
     
    char * Filename_RecoveryInputUser(void)
    {
    	fgets(pFilename, PATH_MAX, stdin); 
    	pFilename[strlen(pFilename)-1] = '\0';
            Keyboard_CleanStdin();
    	return pFilename; 
    }


    Merci désolé si je reviens la dessus.

  14. #14
    Expert éminent sénior
    Citation Envoyé par hbx360 Voir le message
    Bin c'est le code de la faq.
    Bin les posts suivants ont bel et bien prouvé que non. Accessoirement tu remarqueras l'intéressante coïncidence entre ma remarque "Donc s doit être à minima un tableau de caractères soit dynamique (ie char s[1000]), soit alloué, soit provenant de l'appelant." où justement dans la faq, "s" est effectivement provenant de l'appelant (remarque ce n'est pas vraiment une coincidence car il n'y a pas d'autre solution possible)

    Citation Envoyé par hbx360 Voir le message
    Je reviens sur mon code, donc vous me confirmé que ce code est moisie.
    Non, juste quelques maladresses mais pas moisi. Voir mes commentaires directement dans ton code

    Citation Envoyé par hbx360 Voir le message
    Code c :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int main(int argc, char const **argv)
    {
        freopen("CON", "w", stdout);
        printf("Saisir le nom du fichier.\n");
        FILE *pFile = Filename_OpenFile(Filename_RecoveryInputUser(), "r", NULL);
        // Donc on admettra que Filename_RecoveryInputUser() ne peut pas planter. Ok, admettons, c'est pas vraiment grave.
        ......... 
    
        return 0;
    }


    Code c :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
    FILE *Filename_OpenFile(char *pFilename, char *pMode, char *pErrorMsg) {
    	FILE *pFile = fopen(pFilename, pMode); // fopen n'admet pas les '\n'.
    	if(pFile == NULL && pErrorMsg == NULL)
    	{
    		printf("Failed to open file.\n");
    	}
    	else if(pFile == NULL) 
    	{
    		printf("Failed to open file.\n");
    		printf(pErrorMsg);
    		printf("\n");
    	}
            // Deux tests identiques, deux printf identiques, et en plus le test qui décidera si on affiche ou pas pErrorMsg
           // est un test par omission (au lieu de tester directement pErrorMsg, il teste une autre variable qui lui est appariée mais bon, faut le comprendre quoi)
            // Ok, là non plus c'est pas super grave. Juste un peu dommage
    	return pFile;
    }
    Code c :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    FILE *Filename_OpenFile(char *pFilename, char *pMode, char *pErrorMsg) {
    	FILE *pFile = fopen(pFilename, pMode); // fopen n'admet pas les '\n'.
    	if (pFile == NULL) {
    		printf("Failed to open file.\n");
                    if (pErrorMsg != NULL) printf("%s\n", pErrorMsg);
    	}
    	return pFile;
    }

    Simple is better than complex

    Citation Envoyé par hbx360 Voir le message
    Code c :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    char * Filename_RecoveryInputUser(void) {
    	fgets(pFilename, PATH_MAX, stdin); 
    	pFilename[strlen(pFilename)-1] = '\0';        // char *pt; if ((pt=strchr(pFilename)) != NULL) *pt='\0' (même résultat mais plus robuste)
            Keyboard_CleanStdin();
    	return pFilename; 
    }
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  15. #15
    Membre régulier
    Merci pour ton aide.

    J'ai modifié la fonction Filename_RecoveryInputUser j'ai mis pFilename en argument, j'espère que c'est mieux.

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    char * Filename_RecoveryInputUser(char *pFilename)
    {
    	fgets(pFilename, PATH_MAX, stdin); 
           // Ici je mettrai un teste de réussite.
    	pFilename[strlen(pFilename)-1] = '\0'; // Ok je changerai ça aussi.
            Keyboard_CleanStdin(); 
    	return pFilename;
    }


    Entre fgets et pFilename il faudra que je rajoute si j'ai bien compris un test car fgets peu échoué je vois pas bien comment il pourrait renvoyé null mais ok je me servirai de l'ex. de la faq.

    Une autre question quand fgets récupère la chaîne est-ce qu'il avance le curseur du buffer clavier ?
    Par exemple si je rentre salut et que je lui demande de récupéré 1 caractère (donc 's') est-ce que sa veut dire qu'après dans le buffer le curseurs sera sur 'a et que le 's' sera supprimer ou restera t-il dans le buffer clavier ?

    Et quand on appel ensuite CleanStdin est-ce que fgetc (je préfère à getchar) repars du début du buffer ? Donc sur 's' si pas absente.
    Est-ce qu'on peut voir le buffer clavier comme un fichier .txt ?

    Y a t-il une possibilité de réglé le problème de taper 2 fois entrée.

    J'ai énormément de mal à analysé si les programmes que je fais sont pas trop naïf.

  16. #16
    Expert éminent sénior
    Citation Envoyé par hbx360 Voir le message
    J'ai modifié la fonction Filename_RecoveryInputUser j'ai mis pFilename en argument, j'espère que c'est mieux.
    Oui, tant qu'on peut éviter les globales...

    Citation Envoyé par hbx360 Voir le message
    Entre fgets et pFilename il faudra que je rajoute si j'ai bien compris un test car fgets peu échoué je vois pas bien comment il pourrait renvoyé null mais ok je me servirai de l'ex. de la faq.
    fgets() renvoie NULL quand il n'y a plus rien à lire. Ce qui permet donc de quitter la boucle de lecture (surtout ne pas utiliser feof() pour vérifier qu'on a tout lu car cette fonction ne sert pas à ça)

    Citation Envoyé par hbx360 Voir le message
    Une autre question quand fgets récupère la chaîne est-ce qu'il avance le curseur du buffer clavier ?
    Par exemple si je rentre salut et que je lui demande de récupéré 1 caractère (donc 's') est-ce que sa veut dire qu'après dans le buffer le curseurs sera sur 'a et que le 's' sera supprimer ou restera t-il dans le buffer clavier ?
    Heureusement, sinon comment on ferait ?

    Citation Envoyé par hbx360 Voir le message
    Est-ce qu'on peut voir le buffer clavier comme un fichier .txt ?
    Exactement. stdin est identique à tout flux fopen

    Citation Envoyé par hbx360 Voir le message
    Y a t-il une possibilité de réglé le problème de taper 2 fois entrée.
    Faudrait plus de précisions sur ce détail. Quelle fonction tu utilises ? A quel moment ça se produit...
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  17. #17
    Membre régulier
    Citation Envoyé par Sve@r Voir le message

    Y a t-il une possibilité de réglé le problème de taper 2 fois entrée.
    Faudrait plus de précisions sur ce détail. Quelle fonction tu utilises ? A quel moment ça se produit...
    J'utilise les fonctions que j'ai mis plus haut. Quand je rentre au départ sur l'invite de commande le nom du fichier (salut.ass) et que je tape entrée et bien à cause (je pense) de la fonction Keyboard_CleanStdin

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    void Keyboard_CleanStdin(void)
    {
      s32 c;
      do
      {
        // Note : si on met stdin la console attendra toujours que l'utilisateur tape entrée, que ce soit
    		// fgetc ou fgets pour valider.
        c = fgetc(stdin); 
      }while(c != '\n' && c !=EOF);
    }


    il m'oblige à taper une 2e fois entré je pense que fgetc attends comme fgets (sans doute à cause de stdin) un '\n' pour valider la saisie. J'ai la même chose avec getchar.

    Si je ne fait pas CleanStdin, je rentre le nom du fichier puis entrée et je n'ai pas besoin de réappuyer sur entrée.
    Sur un de mes message j'ai mis une vidéo.

  18. #18
    Expert éminent sénior
    Citation Envoyé par hbx360 Voir le message
    Si je ne fait pas CleanStdin, je rentre le nom du fichier puis entrée et je n'ai pas besoin de réappuyer sur entrée.
    Sur un de mes message j'ai mis une vidéo.
    Quand je te demande plus de détails, c'est implicitement pour te demander un exemple concret que je puisse copier/coller pour ensuite examiner ce qui se passe, pas des explications à la "je fais comme ci et puis ça marche pas" etc.

    Parce que par exemple chez-moi, ça marche nickel
    Code c :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
    #include <stdio.h>
     
    void Keyboard_CleanStdin(void) {
    	char c;
    	do {
    		// Note : si on met stdin la console attendra toujours que l'utilisateur tape entrée, que ce soit
    		// fgetc ou fgets pour valider.
    		c=fgetc(stdin); 
    	} while(c != '\n' && c !=EOF);
    }
     
    int main() {
    	char s[1000];
    	int n;
     
    	printf("Chaine :"); fflush(stdout);
    	scanf("%s", s);
    	Keyboard_CleanStdin();
    	printf("s=%s\n", s);
     
    	printf("Nb :"); fflush(stdout);
    	scanf("%d", &n);
    	Keyboard_CleanStdin();
    	printf("n=%d\n", n);
    }


    Accessoirement, quand on maitrise son clavier, c'est généralement inutile d'utiliser ce genre de fonction.
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  19. #19
    Membre régulier
    Voilà un exemple.

    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
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h> // ctypes.h
    #include <limits.h> // Limits chemin.
    #include <string.h> 
     
     
    void Keyboard_CleanStdin(void)
    {
      int c;
      do
      {
        // Note : si on met stdin la console attendra toujour que l'utilisateur tape entrée, que ce soit
    		// fgetc ou fgets pour valider.
        c = fgetc(stdin); 
      }while(c != '\n' && c !=EOF);
    }
     
    char * Filename_RecoveryInputUser(char *pFilename)
    {
    	fgets(pFilename, PATH_MAX, stdin); 
    	pFilename[strlen(pFilename)-1] = '\0'; // A revoir passé par strchr.
      	Keyboard_CleanStdin();
    	return pFilename;
    }
     
    FILE *Filename_OpenFile(char *pFilename, char *pMode, char *pErrorMsg)
    {
    	FILE *pFile = fopen(pFilename, pMode); // fopen n'admet pas les '\n'.
    	if(pFile == NULL)
    	{
    		printf("Failed to open file.\n");
                    if (pErrorMsg != NULL) printf("%s\n", pErrorMsg);
    	}
    	return pFile;
    }
     
    void Filename_WaitingTxt(void)
    {
    	char pInput[2] = {};
    	int i = 0;
     
    	printf("Tapé entrée pour continuer.\n");
    	fgets(pInput, 2, stdin);
    	Keyboard_CleanStdin();
     
    	while( pInput[i] != '\n')
    	{
    		printf("Tapé  entrée pour continuer.\n");
    		fgets(pInput, 2, stdin);
    		Keyboard_CleanStdin();
    	}
    }
     
    int main(int argc, char const **argv)
    {
    	freopen("CON", "w", stdout);
     
    	char pFilename[PATH_MAX]; // Buffer pour récupérer la saisie utilisateur.
    	printf("Saisir le nom du fichier.\n");
    	FILE *pFile = Filename_OpenFile(Filename_RecoveryInputUser(pFilename), "r+", NULL);
    	fclose(pFile);
     
    	Filename_WaitingTxt();
     
    	return 0;
    }

  20. #20
    Expert éminent sénior
    Ok, là j'ai pu tester.
    Donc déjà perso le prompt "entrez le nom du fichier" je le mettrais dans "Filename_RecoveryInputUser". Après-tout, pour une fonction destinée à faire saisir un nom de fichier c'est plutôt normal que ce soit elle qui demande à saisir.
    Sinon le souci c'est que Keyboard_CleanStdin est censée vider le buffer stdin. Ce qui sous-entend que stdin contient des trucs à vider. S'il est vide quand tu l'appelles, alors fgetc(stdin) se comporte comme toute fonction attendant une saisie: elle attend la saisie.
    Reprenons les choses dans le bon sens: tu appelles une fonction de saisie, ça veut dire que tu as envie qu'elle attende que tu tapes des trucs au clavier. Il se trouve que si le clavier contient déjà des trucs alors elle n'attend pas et récupère directement ces trucs. Ce qui généralement là te fait bien chier d'où le vidage préalable à la saisie. Perso je suis plutôt partisan du "si le clavier contient des trucs c'est que soit ces trucs sont importants et donc il ne faut pas le vider (si par exemple mon programme est placé derrière un pipe, tout stdin sera chargé avec le produit du pipe et donc j'ai pas intérêt à le vider) soit j'ai raté une étape lors de la saisie précédente". Et donc dans tous les cas je n'ai pas à vider stdin.

    Maintenant si stdin est vide, alors la fonction se comporte tout à fait normalement et donc attend que tu tapes des trucs. Tu ne peux donc pas lui en vouloir qu'elle attende.
    Et quand tu fais saisir le nom du fichier avec fgets(), tu valides cette saisie en appuyant sur <return>. Là se passe alors 2 choses
    1. le programme se débloque, n'attend plus rien et continue son job donc tu ne peux plus rentrer des trucs dans stdin
    2. fgets() récupère tout jusqu'au '\n' (qui est l'équivalent ascii du <return>) et donc stdin est alors vidé

    Et donc au fgetc() suivant, re-attente.
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site