Thread: Flushing Out fflush(stdin);

  1. #1
    Registered User
    Join Date
    Nov 2007
    Posts
    26

    Lightbulb Flushing Out fflush(stdin);

    Hey folks,

    I have first concentrated on writing functional code that does what I want. However, I have read the FAQ and numerous posts about portability issues with fflush so I have now attempted to replace it with some more reliable, if more complex, code in the program I'm working on.

    Here's what I had. These 3 lines are simple and effective for my purposes:

    Code:
    printf ("\nEnter a new name.\n\n>");
          scanf  ("%[^\n]", entry.name);
          fflush(stdin);
    And here's the best mess I've come up with to replace it:

    Code:
    printf ("\nEnter a new name.\n\n>");
         fgets(entry.name, sizeof(entry.name), stdin);
         if (strchr (entry.name, '\n' && EOF) == NULL)          /* if overflow        */
         {
         printf ("\nYour entry is too long, please try again.");
               while (strchr (entry.name, '\n' && EOF) == NULL) /* keep reading until */
               {                                                /* stdin is empty     */
               fgets(entry.name, sizeof(entry.name), stdin);
               }
               input ();                                        /* recursive          */
         }     
               entry.name[strlen(entry.name) - 1] = ' ';        /* replace \n         */
    I commented what I think it should be/want it to be doing. However, no matter what I enter, the if statements seem to always == NULL. I think this has something to do with the EOF because when I remove it the behavior changes. When I compile with only the newline in the if statements the first if works as I intend but I'm still getting junk from over-length entries in the name field output and the correct second entry from the "try again" is no where to be found.

    Aside from actually fixing the code above, I'm open to any suggestions on how to streamline this as there's likely a much simpler way to do it. Thanks in advance for suggestions and insights. Cheers.

    -SH

  2. #2
    Registered User
    Join Date
    Nov 2007
    Posts
    26
    Okay, I just read about

    Code:
    while((ch = getchar()) != '\n' && ch != EOF);
    In the recent Explanation of a Function thread and some bells are ringing. I'd still appreciate comments on my newline removal or anything else that's looking floppy. Cheers.

    -SH

  3. #3
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Don't recurse, use a "do while" loop - otherwise, someone hanging on the "a" key, hitting enter once in a while, may crash your system.

    As fflush(stdin) is undefined, it's obviously not an option. If all you want to do is "flush" away the newline left behind by scanf(), then a simple getchar() is sufficient.

    Your new code, however, is much more robust to failures, and checks for more possible error conditions than your original code, so it's "better" [and obviously a bit more complex].



    Perhaps:
    Code:
    entry.name[strlen(entry.name) - 1] = '\0';
    Better yet:
    [code]
    char *newline;
    ...
    newline = strchr (entry.name, '\n');
    if (newline)
    *newline = 0;
    else ... // no newline, buffer overflow, clear it out.

    Code:
    strchr (entry.name, '\n' && EOF) == NULL
    The EOF is incorrect here - remove it.

    Code:
         fgets(entry.name, sizeof(entry.name), stdin);
    You should check this for "NULL" return value - that means EOF has been hit. In both places.

    I hope this helps.


    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  4. #4
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    This uses do / while loops to accomplish what I think you are after. Any input longer than the max allowed is eaten and tossed into the bit bucket and the process is repeated until a valid length entry is entered. It also prompts again if no data is entered.

    Code:
    #include <stdio.h>
    #include <string.h> 
      
    int main()
    {
    	char entry [10] ; 
    	int flag ;  
    	do {
    		flag = 1 ;  // assume true (a correct length string is entered) 
    		printf ("\nEnter a new name. (max &#37;d characters)\n\n>", sizeof(entry)-2);
    		fgets(entry , sizeof(entry), stdin);
    		do { 
    			if ( strchr(entry, '\n') == NULL ) { 
    				entry[0] = '\0' ; // get rid of string if too long 
    				flag = 0 ;        // set to false - string was too long 
    				fgets(entry , sizeof(entry), stdin); // read any remaining buffer into bit bucket 
    			}
    		} while (strchr(entry, '\n') == NULL) ;  
    		if (strlen(entry) == 1) flag = 0 ;   // empty string - only ENTER was pressed.  
    	} while ( !flag )  ;  
        entry[(strchr(entry,'\n')-entry)] = '\0';       // replace \n with null term  	 
    	printf("entry = >%s<\n", entry) ; 
    	return 0 ; 
    }

  5. #5
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    That will loop forever if you hit CTRL-Z (or CTRL-D in Linux), as you've just hit End of file, and fgets() will return nothing. You'll need to check for NULL on first the call to fgets() [it doesn't matter much on the second one, as it's not "important".

    Also:
    Code:
        entry[(strchr(entry,'\n')-entry)] = '\0';       // replace \n with null term
    If you end up here due to a EOF, you won't necessarily have a newline in the string, and thus the application will crash when it tries to access "entry" items into the string - usually far too big to access correctly...

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  6. #6
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    I'm on a Mac. CTRL+Z does nothing. CTRL+D does nothing. CTRL+C ends up with a SIGINT and kills the app.

    How's this? :
    Code:
    #include <stdio.h>
    #include <string.h> 
      
    int main()
    {
    	char entry [10] ; 
    	int flag ; 
    	char *fgets_ptr ;  
    	do {
    		flag = 1 ;  // assume true (a correct length string is entered) 
    		printf ("\nEnter a new name. (max &#37;d characters)\n\n>", sizeof(entry)-2);
    		fgets_ptr = fgets(entry , sizeof(entry), stdin);
    		if (fgets_ptr==NULL) return -1 ; 
    		do { 
    			if ( strchr(entry, '\n') == NULL ) { 
    				entry[0] = '\0' ; // get rid of string if too long 
    				flag = 0 ;        // set to false - string was too long 
    				fgets_ptr = fgets(entry , sizeof(entry), stdin); // read any remaining buffer into bit bucket 
    				if (fgets_ptr==NULL) return -1 ; 
    			}
    		} while (strchr(entry, '\n') == NULL) ;  
    		if (strlen(entry) == 1) flag = 0 ;   // empty string - only ENTER was pressed.  
    	} while ( !flag )  ;  
        entry[(strchr(entry,'\n')-entry)] = '\0';       // replace \n with null term  	 
    	printf("entry = >%s<\n", entry) ; 
    	return 0 ; 
    }

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I'm on a Mac. CTRL+Z does nothing. CTRL+D does nothing.
    I believe CRTL+D simulates EOF on a Mac. Are you sure you are pressing the correct control key?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    I've tried CONTROL+D and CMD+D. Neither do it. I'm running under XCode. Could XCode be intercepting it and disabling it?

  9. #9
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I'm sure some sort of keypressing will make a "end-of-file" on stdin even on a Mac, but that may be neither here not there - it's certainly possible in other environments, and your code fixes that particular problem.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  10. #10
    Registered User
    Join Date
    Nov 2007
    Posts
    26
    Wowzers, thanks for the very informative replies. I'm still boggling at how much this code (or something like it) increases both the size and complexity of my once innocent little prog... growing pains.
    Cheers.

    -SH

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Yes, but once you've written one function that takes a generic input and verifies it, you can call it every place you need it, in a single line. You just have to move out the knowledge of what the string is for (e.g. the question of name), the input buffer and the size, and re-use it many times.

    Edit:
    Code:
    		do { 
    			if ( strchr(entry, '\n') == NULL ) { 
    				entry[0] = '\0' ; // get rid of string if too long 
    				flag = 0 ;        // set to false - string was too long 
    				fgets(entry , sizeof(entry), stdin); // read any remaining buffer into bit bucket 
    			}
    		} while (strchr(entry, '\n') == NULL) ;
    can be written like this:
    Code:
    		if ( strchr(entry, '\n') == NULL ) { 
                        int ch;
                        while((ch = getchar()) != '\n' && ch != EOF)  /* do nothing */ ;
                        flag = 0;
                    }
    It's a bit shorter, and does only one set of strchr, no stuffing input into a string, etc.

    --
    Mats
    Last edited by matsp; 11-26-2007 at 08:52 AM.
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #12
    Registered User
    Join Date
    Nov 2007
    Posts
    26
    Yeah, that's what I was going for, thanks for fleshing it out Todd and matsp. I'm still not quite sure what this line does at the end of the do blocks in Todd's code:

    Code:
    if (fgets_ptr==NULL) return -1 ;
    I removed it and the function still seems to work fine. I'm sure you put it there for something...

    Also, I'm puzzled by:

    Code:
    entry[(strchr(entry,'\n')-entry)] = '\0';
    What's the -entry for? Isn't it enough to insert a null char at the location of the newline as found by (strchr(entry,'\n')?
    Thanks again.

    -SH

  13. #13
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by SiliconHobo View Post
    Yeah, that's what I was going for, thanks for fleshing it out Todd. I'm still not quite sure what this line does at the end of the do blocks:

    Code:
    if (fgets_ptr==NULL) return -1 ;
    I removed it and the function still seems to work fine. I'm sure you put it there for something...
    It checks if there was a "end of file" situation whilst trying to read the input - if you remove it, fgets() will return NULL, and because fgets() is not reading the string and never getting any input into the string, so it will loop forever. Try hitting a "ctrl-Z" or "ctrl-D" when you input with it there and with it removed.

    Also, I'm puzzled by:

    Code:
    entry[(strchr(entry,'\n')-entry)] = '\0';
    What's the -entry for? Isn't it enough to insert a null char at the location of the newline as found by (strchr(entry,'\n')?
    Thanks again.

    -SH
    The -entry is there to give you an index of the newline, rather than the ADDRESS of thre newline, which is what the strchr() returns. It inserts a NUL (note a single L) at the newline location in the string. You can do that in different ways. You could actually do:
    Code:
    *(strchr(entry, '\n')) = 0;
    if you want to be obscure. But it's not exactly readable [neither is the above, strictly speaking]. I prefer a construction like this:
    Code:
    size_t len = strlen(entry);
    if (entry[len-1] == '\n') 
           entry[len-1] = '\0';
    It's cleaner than the strchr() - entry, and also copes with non-newline ending lines without any problem.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #14
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    I too thank you Matsp! You serve this forum well. Lots to learn from you.

    Quote Originally Posted by matsp View Post
    I prefer a construction like this:
    Code:
    size_t len = strlen(entry);
    if (entry[len-1] == '\n') 
           entry[len-1] = '\0';
    It's cleaner than the strchr() - entry, and also copes with non-newline ending lines without any problem.
    But, by the definition of fgets() (it stop at a newline, or EOF), there's no point in testing to see if a newline exists. If a newline does not exist, the flow will never get here. So, isn't testing for a newline at this point in this logic redundant?

    Todd

  15. #15
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Todd Burch View Post
    I too thank you Matsp! You serve this forum well. Lots to learn from you.



    But, by the definition of fgets() (it stop at a newline, or EOF), there's no point in testing to see if a newline exists. If a newline does not exist, the flow will never get here. So, isn't testing for a newline at this point in this logic redundant?

    Todd
    Thanks for the kind words. :blush:

    Yes, it'sredundant - but you'd save about 5 instructions for the if-statement, out of a thousand [fgets() will take hundreds of instructions for each char being read in] - and strchr() is probably no more efficient than strlen(), so it's no better than that [in fact, strchr is most likely LESS efficient than strlen()]. Belts and braces...

    --
    mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Another syntax error
    By caldeira in forum C Programming
    Replies: 31
    Last Post: 09-05-2008, 01:01 AM
  2. fread/fwrite
    By chopficaro in forum C Programming
    Replies: 6
    Last Post: 05-11-2008, 01:48 AM
  3. Searching Into Files
    By St0rM-MaN in forum C Programming
    Replies: 12
    Last Post: 04-26-2007, 09:02 AM
  4. Newbie Question - fflush(stdin) & fpurge(stdin) on Mac and PC
    By tvsinesperanto in forum C Programming
    Replies: 34
    Last Post: 03-11-2006, 12:13 PM
  5. Linked List Need Help Urgent
    By ykchua in forum C Programming
    Replies: 5
    Last Post: 08-17-2004, 02:57 PM