Thread: fgets causing problems

  1. #1
    Registered User
    Join Date
    Mar 2012
    Posts
    26

    fgets causing problems

    I'm having problems with my code here (assume correct declaration of variables):



    Code:
        int i, Done = 0;
        
        while (Done == 0) {
            
            printf("  Input currency you have: ");
            fgets((*zzz).Currency, 4, stdin);
            
            for (i = 0; i < Count; i++) {
                if (strcmp((*zzz).Currency, aaa[i].Currency) == 0) {
                    (*zzz).Exchange = aaa[i].Exchange;
                    break;
                }
            }
            
            if (i != Count) {
                printf("Proceed!");
                break;
            }
            
            else {
                printf("Wrong!");
                continue;
            }
    Basically, what happens is the else statement gets run twice. I suspect fgets is the cause, because previously, I was using scanf and I encountered no such problems.

    What am I doing wrong?

  2. #2
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    fgets() leaves a trailing '\n' on its input. The behaviour of scanf() depends on your choice of format string, but generally it does not leave a trailing '\n' on inputs.

    Incidentally (*zzz).Currency is generally appreviated to zzz->Currency. The purpose of the -> operator is to do exactly that in a less complicated manner. It is often considered bad practice to use break and continue statements as you are (effectively you are using them as goto statements).
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  3. #3
    Registered User
    Join Date
    Mar 2012
    Posts
    26
    Hmm, so you're saying I should change the number parameter in fgets to something else? I've tried doing that, but the string has isn't exactly the same anymore (i.e. whitespaces exist at the end of the string).

    But isn't the goto statement...too specific? Like if you tell it to goto this certain line... it does go there. So if you change your source code later on, it may not work as intended anymore. Whereas with break and continue, you just have to be wary of the scope of your loops. Regardless, what do you suggest I use instead of break and continue?

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    You should also read input into a large buffer.
    If you have only 4 characters in your array (one of them will be the \0), then normally fgets() has only room for 2 characters AND the \n.

    But if you type in 3 characters, then fgets() returns WITHOUT storing a \n, but will then return immediately next time with the \n left over from last time.

    So
    Code:
    char buff[BUFSIZ];
    fgets( buff, sizeof(buff), stdin );
    // Now you validate the input
    // Remove the \n (if it's important)
    // check the length (is it long/short enough to be a currency)
    // does it only contain alpha characters
    // If all is well, then copy it to where you need it to be.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Mar 2012
    Posts
    26
    Why is it that when I use fgets twice in a row (not in the same function), the second instance of fgets doesn't work properly? By that, I mean it's only able to get like 1 character or so.

  6. #6
    Registered User
    Join Date
    Jun 2010
    Location
    Michigan, USA
    Posts
    143
    Have you read the documentation for fgets()? For example from here. How much does it read on each call?

  7. #7
    Registered User
    Join Date
    Mar 2012
    Posts
    26
    First try, 1 character. Second try, 3 characters.

  8. #8
    Registered User
    Join Date
    Jun 2010
    Location
    Michigan, USA
    Posts
    143
    Is that what your experiments tell you? If so, what does the documentation tell you?

  9. #9
    Registered User
    Join Date
    Mar 2012
    Posts
    26
    My 2 strings can contain 4 chars. And I used sizeof operator for the n (which is equal to 4). So that means fgets stops reading when it has read 3 characters (or bytes). Which is my intent.

    Should I do flush stdin between every fgets operation?
    Last edited by Jyqft; 03-25-2012 at 09:08 AM.

  10. #10
    Registered User
    Join Date
    Jun 2010
    Location
    Michigan, USA
    Posts
    143
    In the documentation, there is an important "or". You seem to be ignoring it. (I gave you a link to one version of the documentation in message 6.) What is after the "or"?

    flush stdin is undefined behavior in the C standard. Some implementations (compiler with associated library) support it to do what I think you are thinking.
    Last edited by pheininger; 03-25-2012 at 09:18 AM. Reason: Add comment about flush(stdin);. Changed from compiler to implementation.

  11. #11
    Registered User
    Join Date
    Mar 2012
    Posts
    26
    OR A newline is read and transferred to s. So that means the '\n' is also transferred to the first string.

    So if the user input 'abc', it'll now contain:
    Code:
    {'a', 'b', 'c', '\n'}
    Yes?

  12. #12
    Registered User
    Join Date
    Jun 2010
    Location
    Michigan, USA
    Posts
    143
    No.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAX_BUFF 100
    #define NCHAR_PER 4
    
    int main(int argc, char *argv[])
    {
    
       char s[MAX_BUFF];
       char * result;
    
       result = fgets(s, NCHAR_PER, stdin);
       if (result == NULL) {
          return EXIT_FAILURE;
       }
       else {
          fprintf(stdout, "strlen(s) = %u s = %s\n", strlen(s), s);
          result = fgets(s, NCHAR_PER, stdin);
          if (result == NULL) {
             return EXIT_FAILURE;
          }	
          else {
             fprintf(stdout, "strlen(s) = %u s = %s\n", strlen(s), s);
          }
       }
       return EXIT_SUCCESS;
    }
    With an input of:
    abc\n

    will have return with the string of
    Code:
    {'a', 'b', 'c', '\0'}
    on the first call
    and a string of
    Code:
    {'\n', '\0'}
    on the second call.

  13. #13
    Registered User
    Join Date
    Mar 2012
    Posts
    26
    stdin is like a temporary storage for the input before it gets transferred to the string right?

    So that means if I regularly flush stdin, the second fgets instance won't go haywire anymore?

  14. #14
    Registered User
    Join Date
    Jun 2010
    Location
    Michigan, USA
    Posts
    143
    Remember:

    1. fflush(stdin) is not required to be supported on all implementations and may be ignored, give an error or do some other unexpected behavior.
    2. Make sure you are going to deal with (if necessary) input like "abc d e f g xyz" without the quotations and other variations.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Stringstream data type causing problems
    By Boomn4x4 in forum C++ Programming
    Replies: 13
    Last Post: 01-13-2012, 04:35 PM
  2. problems with fgets
    By marcoesteves in forum C Programming
    Replies: 11
    Last Post: 01-20-2011, 08:13 AM
  3. MinGW causing problems with sprintf()
    By sedavidw in forum C Programming
    Replies: 6
    Last Post: 01-14-2010, 05:37 AM
  4. sscanf causing problems
    By takamineg240 in forum C++ Programming
    Replies: 10
    Last Post: 09-26-2006, 04:07 AM
  5. Class member using a global variable, causing problems.
    By RealityFusion in forum C++ Programming
    Replies: 1
    Last Post: 09-11-2005, 11:27 PM