Thread: fgets() question

  1. #1
    Registered User
    Join Date
    Jan 2005
    Posts
    204

    fgets() question

    A friend of mine is trying to write this simple C program and he's having a problem with the fgets() function. He's also brazilian but he doesn't speak english, that's why I'm asking for him. Take a look at his code, please:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    void flush_newline(char *);
    int process_choice(char *);
    int valid_menu_choice(char *);
    
    int main()
    {
       char line[5];
    
       do{
          printf("[1] Cadastrar cliente\n");
          printf("[2] Visualizar ficha\n");
          printf("[3] Sair\n\n");
    
          printf("> ");
          fgets(line, sizeof line, stdin);
          flush_newline(line);
       }while(process_choice(line) != 3);
    
       return 0;
    }
    
    
    
    
    /*____________________________________________________________________
    | flush_newline(): retira o \n do final da string se o mesmo existir. |
    |                                                     |
    | Parâmetros: a string a ser testada.                          |
    |_____________________________________________________________________|*/
    void flush_newline(char *line)
    {
       char *pointer;
    
       if((pointer = strchr(line, '\n')) != NULL)
          *pointer = '\0';
    }
    
    
    
    
    
    /*______________________________________________________________________________________________
    | process_choice(): Valida e, se tudo estiver certo, processa a opção escolhida pelo usuário    |
    |                   no menu principal.                                                          |
    |                                                                                               |
    | Parâmetros:                                                                                   |
    |     line: texto digitado pelo usuário no prompt do menu principal.                            |
    |                                                                                               |
    | Retorna:                                                                                      |
    |     Se o input do usuário não for uma opção válida, a função retorna o número 0. Dessa forma, |
    |     o menu e o prompt vão ser mostrados outra vez na tela por causa do loop na função main(). |
    |_______________________________________________________________________________________________|*/
    int process_choice(char *line)
    {
       int menu_choice;
       
       if((menu_choice = valid_menu_choice(line)) == 0)
          return 0;
    
       if(menu_choice == 3)
          exit(1);
    }
    
    
    
    
    /*_________________________________________________________________________________________________
    | valid_menu_choice(): Valida o input do usuário de acordo com as opções do menu principal.         |
    |                                                                           |
    | Parâmetros:                                                                  |
    |     line: texto digitado pelo usuário no prompt do menu principal.                        |
    |                                                                           |
    | Retorna:                                                                     |
    |     Se o input do usuário não for válido, a função retorna o número 0, o que vai causar a função |
    |     process_choice() retornar 0 também. O menu e o prompt vão ser mostrados outra vez na tela.   |
    |                                                                           |
    |     Se o input for válido, ele será convertido para int e retornado.                        |
    |__________________________________________________________________________________________________|*/
    int valid_menu_choice(char *line)
    {
       size_t i;
       int menu_choice;
    
       if(line[0] == '\0'){
          printf("Comando desconhecido.\n\n");
          return 0;
       }
    
       for(i = 0; i < strlen(line); i++){
          if(!isdigit(line[i])){
             printf("Comando desconhecido.\n\n");
             return 0;
          }
       }
    
       sscanf(line, "%d", &menu_choice);
    
       return menu_choice;
    }
    At the prompt, if you type something with more than four characters, look what happens:
    Code:
    [1] Cadastrar cliente
    [2] Visualizar ficha
    [3] Sair
    
    > qqqqqqqqqqqqqqq
    Comando desconhecido.
    
    [1] Cadastrar cliente
    [2] Visualizar ficha
    [3] Sair
    
    > Comando desconhecido.
    
    [1] Cadastrar cliente
    [2] Visualizar ficha
    [3] Sair
    
    > Comando desconhecido.
    
    [1] Cadastrar cliente
    [2] Visualizar ficha
    [3] Sair
    
    > Comando desconhecido.
    
    [1] Cadastrar cliente
    [2] Visualizar ficha
    [3] Sair
    
    > Comando desconhecido.
    
    [1] Cadastrar cliente
    [2] Visualizar ficha
    [3] Sair
    
    > Comando desconhecido.
    
    [1] Cadastrar cliente
    [2] Visualizar ficha
    [3] Sair
    
    >
    Wasn't fgets() supposed to take care of that? I couldn't help him but I hope one of you can. Thanks in advance.

  2. #2
    & the hat of GPL slaying Thantos's Avatar
    Join Date
    Sep 2001
    Posts
    5,681
    fgets() will remove num - 1 characters from the input stream and put them into the buffer. The problem is that if you type more then that number of characters then the rest will be left in the buffer and will immediatly be picked up by the next call to fgets(). What needs to be done is to remove the excess characters from the input stream if they exceeded the max size of your buffer. Something like:
    Code:
    void flush_newline(char *line)
    {
       char *pointer;
    
       if((pointer = strchr(line, '\n')) != NULL)
          *pointer = '\0';
       else
         /* Flush the input buffer */
    }

  3. #3
    Registered User
    Join Date
    Aug 2004
    Posts
    77
    Looking at it everything looks like its working fine, I didn't try to run it though. But this code:
    Code:
    for(i = 0; i < strlen(line); i++){
          if(!isdigit(line[i])){
             printf("Comando desconhecido.\n\n");
             return 0;
          }
       }
    Checks the input to see if the first character is a digit, since its not it (you typed q's i nthe example) it prints that message and returns.

    fgets() then grabs the remainig characters in the buffer (the q's past the first 5) and moves on. You need t oclear out those extra characters in the buffer.

    I think you can do that by calling fflush() right before the fgets() call. But I've been reading a lot of bad stuff about fflush() and its uses so I dunno if thats the best route to take.
    Last edited by Exile; 02-01-2005 at 02:53 PM.

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    204
    Ok, I changed his code to this:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    void flush_newline(char *);
    int process_choice(char *);
    int valid_menu_choice(char *);
    
    int main()
    {
        char line[5];
    
        do{
            printf("[1] Cadastrar cliente\n");
            printf("[2] Visualizar ficha\n");
            printf("[3] Sair\n\n");
    
            printf("> ");
            fgets(line, (sizeof line) - 1, stdin);
            flush_newline(line);
        }while(process_choice(line) != 3);
    
        return 0;
    }
    
    
    
    
    /*______________________________________________________________________________________
    | flush_newline(): retira o \n do final da string se o mesmo existir ou limpa o buffer. |
    |                                                                                        |
    | Parâmetros: a string a ser testada.                                                    |
    |_______________________________________________________________________________________|*/
    void flush_newline(char *line)
    {
        char ch;
        char *pointer;
    
        if((pointer = strchr(line, '\n')) != NULL)
            *pointer = '\0';
    
        else{
            while((ch = getchar()) != '\n')
                ch = getchar();
        }
    }
    
    
    
    
    
    /*______________________________________________________________________________________________
    | process_choice(): Valida e, se tudo estiver certo, processa a opção escolhida pelo usuário    |
    |                   no menu principal.                                                          |
    |                                                                                               |
    | Parâmetros:                                                                                   |
    |     line: texto digitado pelo usuário no prompt do menu principal.                            |
    |                                                                                               |
    | Retorna:                                                                                      |
    |     Se o input do usuário não for uma opção válida, a função retorna o número 0. Dessa forma, |
    |     o menu e o prompt vão ser mostrados outra vez na tela por causa do loop na função main(). |
    |                                                                                                |
    |      Se o input for válido e igual ao número 3, o número 3 é retornado e o programa termina.   |
    |_______________________________________________________________________________________________|*/
    int process_choice(char *line)
    {
        int menu_choice;
        
        if((menu_choice = valid_menu_choice(line)) == 0)
            return 0;
    
        else if(menu_choice == 1){
            printf("Cadastrar cliente.\n\n");
            return 0;
        }
    
        else if(menu_choice == 2){
            printf("Visualizar ficha.\n\n");
            return 0;
        }
    
        else
            return 3;
    }
    
    
    
    
    /*_________________________________________________________________________________________________
    | valid_menu_choice(): Valida o input do usuário de acordo com as opções do menu principal.           |
    |                                                                                                   |
    | Parâmetros:                                                                                       |
    |     line: texto digitado pelo usuário no prompt do menu principal.                               |
    |                                                                                                   |
    | Retorna:                                                                                           |
    |     Se o input do usuário não for válido, a função retorna o número 0, o que vai causar a função |
    |     process_choice() retornar 0 também. O menu e o prompt vão ser mostrados outra vez na tela.   |
    |                                                                                                   |
    |     Se o input for válido, ele será convertido para int e retornado.                               |
    |__________________________________________________________________________________________________|*/
    int valid_menu_choice(char *line)
    {
        size_t i;
        int menu_choice;
    
        if(line[0] == '\0'){
            printf("Comando desconhecido.\n\n");
            return 0;
        }
    
        for(i = 0; i < strlen(line); i++){
            if(!isdigit(line[i])){
                printf("Comando desconhecido.\n\n");
                return 0;
            }
        }
    
        sscanf(line, "%d", &menu_choice);
    
        if((menu_choice <= 0) || (menu_choice > 3)){
            printf("Comando desconhecido.\n\n");
            return 0;
        }
    
        return menu_choice;
    }
    It worked, but now there's just a little detail. Look at this:
    Code:
    [1] Cadastrar cliente
    [2] Visualizar ficha
    [3] Sair
    
    > 111111111111111
    Comando desconhecido.
    
    [1] Cadastrar cliente
    [2] Visualizar ficha
    [3] Sair
    
    > 111111111111111111111111
    
    Comando desconhecido.
    
    [1] Cadastrar cliente
    [2] Visualizar ficha
    [3] Sair
    
    >
    If I start the program, type a bunch of ones and press enter, the error message will be displayed ("Comando desconhecido"). If I do the same thing on the second prompt, I have to hit enter twice in order to get the error message, and it stays like that until I input something good. Why is it happening? Sorry about the silly questions

  5. #5
    Registered User
    Join Date
    Jan 2005
    Posts
    204
    Nevermind. We got it...
    Code:
    void flush_newline(char *line)
    {
    	char *pointer;
    
    	if((pointer = strchr(line, '\n')) != NULL)
    		*pointer = '\0';
    
    	else
    		while(!feof(stdin) && getchar() != '\n');
    }

  6. #6
    & the hat of GPL slaying Thantos's Avatar
    Join Date
    Sep 2001
    Posts
    5,681
    remove the
    Code:
     ch = getchar();
    from the body of the loop.

  7. #7
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    >> I have to hit enter twice in order to get the error message, and it stays like that until I input something good. <<

    Consider what is happening here:
    Code:
            while((ch = getchar()) != '\n')
                ch = getchar();
    Let's unroll it:
    Code:
    // Input left over in buffer is "a\n"
    
    // First time in the loop...
    ch = getchar(); // get 'a'
    if (ch != '\n') getchar(); // get '\n'
    
    // Second time in the loop...
    ch = getchar(); // Whoops, we're trying to get an extra character!
    if (ch != '\n') getchar(); // Not run if you pressed enter.

  8. #8
    ---
    Join Date
    May 2004
    Posts
    1,379
    Code:
    fgets(line, sizeof(line), stdin);   /* RIGHT */
    
    fgets(line, sizeof line, stdin);    /* WRONG */
    fgets(line, (sizeof line), stdin);  /* WRONG */

  9. #9
    End Of Line Hammer's Avatar
    Join Date
    Apr 2002
    Posts
    6,231
    >>char ch;
    >>ch = getchar()
    getchar returns and int, not a char. Get it wrong, and you'll run into trouble at some point!

    http://faq.cprogramming.com/cgi-bin/...&id=1043284351
    When all else fails, read the instructions.
    If you're posting code, use code tags: [code] /* insert code here */ [/code]

  10. #10
    & the hat of GPL slaying Thantos's Avatar
    Join Date
    Sep 2001
    Posts
    5,681
    Quote Originally Posted by sand_man
    Code:
    fgets(line, sizeof(line), stdin);   /* RIGHT */
    
    fgets(line, sizeof line, stdin);    /* WRONG */
    fgets(line, (sizeof line), stdin);  /* WRONG */
    () are only required with sizeof when you are refering to a type.
    So
    Code:
    int x;
    printf("%d\n", sizeof x);
    printf("%d\n", sizeof(int);
    are both correct but
    Code:
    printf("%d\n", sizeof int);
    Would be incorrect.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. fgets question
    By mattyg in forum C Programming
    Replies: 2
    Last Post: 12-01-2008, 04:25 AM
  2. Question on using fgets
    By gp364481 in forum C Programming
    Replies: 6
    Last Post: 10-17-2008, 10:23 AM
  3. Question...
    By TechWins in forum A Brief History of Cprogramming.com
    Replies: 16
    Last Post: 07-28-2003, 09:47 PM
  4. fgets problem (i think)
    By GanglyLamb in forum C Programming
    Replies: 3
    Last Post: 03-19-2003, 11:19 AM
  5. opengl DC question
    By SAMSAM in forum Game Programming
    Replies: 6
    Last Post: 02-26-2003, 09:22 PM