Thread: fgets question

  1. #1
    Registered User
    Join Date
    Dec 2005
    Posts
    119

    fgets question

    I've been working on the code below for a little whlie, and I've almost got it done good, but there is still one thing nagging me. As the code stands now, if the user
    hits a number > 3 or a letter, it works fine. But if they just hit "enter", they have to hit it twice in order for the corresponding error message to display. It's not a big deal,
    but I'd like to know why that is and if there is any way to fix that. My theory is that since you have to hit enter to terminate fgets, if you just hit enter, it reads in \n,
    and requires another enter to terminate, that's the only thing I can think of....


    Code:
    #include <stdio.h>
    #include <stdlib.h>
    void cls() {
    
    	printf("\x1b[2J");
    
    			   
    }
    
    void cb() {
    	int cb;
    	while ((cb = getchar()) != '\n');
    	return;
    }
    
    
    void intro();
    
    void menu();
    
    int main() {
    
    	printf("\x1b[0m");
    	printf("\x1b[42m");
    	cls();
    	
    
    	intro();
    
    	menu();
    
    	
    
    	
    
    
    
    	return 0;
    }	
    
    
    void intro() {
    
    	cls();
    
    	printf("\x1b[31m");
    
    	printf("\n\n\n\n\n\n\n\n\n\n\n\n\n");
    printf("                     Welcome to Smith Sayings - Freeware Edition\n");
    	printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
    	printf("\x1b[34m");
    	printf("                            Press <ENTER> to continue...\n");
    
    	getchar();
    	printf("\x1b[0m");
    return;
    
    }
    		
    
    
    void menu() {
    	int choice;
    	char ui[2]; //ui = user input
    	cls();
    
    	printf("\x1b[34m");
    	
    	printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
    	printf("                         ***********Main Menu**********                        ");
    	printf("                          *                            *                        ");
    	printf("                          *\x1b[31m         1)\x1b[34mRun Story        *                        ");
    	printf("                          *                            *                        ");
    	printf("                          *                            *                        ");
    	printf("                          *                            *                        ");
    	printf("                          *                            *                        ");
    	printf("                          *\x1b[31m         2)\x1b[34mAbout            *                        ");
    	printf("                          *                            *                        ");
    	printf("                          *                            *                        ");
    	printf("                          *                            *                        ");
    	printf("                          *                            *                        ");
            printf("                          *\x1b[31m         3)\x1b[34mExit             *                        ");
    	printf("                          *                            *                        ");
            printf("                          ******************************                        ");
    
    	printf("\n\n\n\n");
    	printf("                                 Choice:");											
    
    	fgets(ui,2,stdin);
        
    
    
    
    	cb();
    
    
    
    	if (sscanf(ui,"%d",&choice) != 1) {
    
    		cls();
    		printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
    		printf("                         Please enter a valid selection\n");
    		printf("\n                                   Press <ENTER>\n");
    		getchar();
    		menu();
    		} 
    
                                                        
    	if (choice == 1) {
    
            printf("Choice 1 selected");
            getchar();
    		
    		exit(0);
    	}	
    
    	if (choice == 2) {
    
    		cls();
    		printf("\x1b[32m");
    		printf("\x1b[34m\n\n\n\n\n\n\n\n\n\n\n\n\n%25sSmith Sayings Freeware Version\n","");
    		printf("\n%25sCreated by \x1b[32mAsh Smith\x1b[34m\n","");
    		printf("\n%25sEmail:\x1b[[email protected]\x1b[34m\n","");
    		printf("\n\n\n\n%25sPress <ENTER> to return to the main menu\n","");
    		getchar();
    		menu();
    	}
    
    	if (choice == 3) {
    
    		printf("Exit selected");
            getchar();
    		exit(0);
    
    	}
    
    	else {
    
    		cls();
    		printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
    		
    		printf("                         Please enter a number between 1 and 3\n");
    		printf("\n                                      Press <ENTER>");
    		getchar();
    		menu();
    
    	}
    	
    
    		
    
    
    return;
    }

  2. #2
    old man
    Join Date
    Dec 2005
    Posts
    90
    Well, you ask for another input in cb() ...

    Why don't you just check to see if *ui is '\n' after you do the fgets() ?

    I don't know why you need sscanf() at all ... just take *ui as your response and then do a switch statement on it.

  3. #3
    Sr. Software Engineer filker0's Avatar
    Join Date
    Sep 2005
    Location
    West Virginia
    Posts
    235

    Just a note:

    You're doing a lot of printing of "\n" to format the screen. You could save a lot of spaces by using ANSI cursor addressing. You can home the cursor with "\x1b[H" (that moves it to the top-left corner of the screen.) You can move the cursor to any position on the screen by using <esc>[<row>;<column>H, where <esc> is the escape character, <row> is the row number represented as a string of ASCII decimal digits, and <column> is the column number, represented (again) as ASCII decimal digits.

    Do a Google search on "ANSI Control Sequences VT100" to find a reference listing lots of common ANSI screen control sequences.

    I don't have the time to wade through all your screen formatting stuff to offer much assistance at the moment, but I will note that mixing getchar() with fgets() is not a good idea; I'd go as far to say that it's a bad idea to mix them. fgets() takes a file pointer, getchar() always works on stdin. If you want fgets() without the f, use gets(), which gets a string from stdin. Note that gets() does not leave the newline ('\n') in the buffer the way that fgets() does, nor does it limit the number of characters read to fit in the buffer, so it's not as safe.

    I hope this helps a little.
    Last edited by filker0; 01-09-2006 at 04:48 PM. Reason: Remove reference to scanf(), as it was due to a misreading of the posting.
    Insert obnoxious but pithy remark here

  4. #4
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Quote Originally Posted by filker0
    I don't have the time to wade through all your screen formatting stuff to offer much assistance at the moment, but I will note that mixing getchar() with fgets() is not a good idea; I'd go as far to say that it's a bad idea to mix them. fgets() takes a file pointer, getchar() always works on stdin. If you want fgets() without the f, use gets(), which gets a string from stdin. Note that gets() does not leave the newline ('\n') in the buffer the way that fgets() does, nor does it limit the number of characters read to fit in the buffer, so it's not as safe.

    I hope this helps a little.
    Here's a quick little tip for you: If anyone suggests you use gets, it's safe to ignore everything they post.

    Also, rather than just say: "Use fgetc instead of getchar, so you can make sure you're working on the same stream." They instead tell you to simply not use getchar, and then go on to suggest the use of gets as a replacement.


    Quzah.
    Hope is the first step on the road to disappointment.

  5. #5
    old man
    Join Date
    Dec 2005
    Posts
    90
    Quote Originally Posted by filker0
    I will note that mixing getchar() with fgets() is not a good idea; I'd go as far to say that it's a bad idea to mix them. fgets() takes a file pointer, getchar() always works on stdin.
    Considering that stdin is a file pointer, what is being mixed?

    edit:

    This will help clarify what I mean (from stdio.h):

    Code:
    typedef struct _IO_FILE FILE;
    
    ...
    
    /* Standard streams.  */
    extern struct _IO_FILE *stdin;		/* Standard input stream.  */
    extern struct _IO_FILE *stdout;		/* Standard output stream.  */
    extern struct _IO_FILE *stderr;		/* Standard error output stream.  */
    In any case, stdin is still stdin no matter which function uses it.
    Last edited by eerok; 01-10-2006 at 02:58 AM.

  6. #6
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    getchar() is defined (on my compiler) as
    Code:
    #define getchar() getc(stdin)
    You're doing a lot of printing of "\n" to format the screen. You could save a lot of spaces by using ANSI cursor addressing. You can home the cursor with "\x1b[H" (that moves it to the top-left corner of the screen.) You can move the cursor to any position on the screen by using <esc>[<row>;<column>H, where <esc> is the escape character, <row> is the row number represented as a string of ASCII decimal digits, and <column> is the column number, represented (again) as ASCII decimal digits.

    Do a Google search on "ANSI Control Sequences VT100" to find a reference listing lots of common ANSI screen control sequences.
    BTW, those sequences only work under Windows, and then only if you have ANSI.SYS installed (which I don't).
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  7. #7
    Sr. Software Engineer filker0's Avatar
    Join Date
    Sep 2005
    Location
    West Virginia
    Posts
    235
    Quote Originally Posted by eerok
    Considering that stdin is a file pointer, what is being mixed?
    It depends on your library implementation. MS used to get it wrong, so calls to fgets() mixed with getchar() would not always update file pointers as you might expect, especially when using redirection. I'm sure they fixed it, but it was a rule that was pounded into me back in the late 1980s through the 1990s when dealing with various environments. Restated, if you use the f forms of the functions, don't mix them (on the same channel) with the stdout specific ones.
    Insert obnoxious but pithy remark here

  8. #8
    Sr. Software Engineer filker0's Avatar
    Join Date
    Sep 2005
    Location
    West Virginia
    Posts
    235
    Quote Originally Posted by quzah
    Here's a quick little tip for you: If anyone suggests you use gets, it's safe to ignore everything they post.
    If you read the post, I point out that gets() is unsafe. I never use it myself. Perhaps I could have been a bit clearer.

    quzah's advice to ignore everything I write is annoying. He doesn't think I know what I'm talking about, but then again, he seems to think that nobody knows what they're talking about other than him, so I don't feel singled out.
    Insert obnoxious but pithy remark here

  9. #9
    Sr. Software Engineer filker0's Avatar
    Join Date
    Sep 2005
    Location
    West Virginia
    Posts
    235
    Quote Originally Posted by dwks
    BTW, those sequences only work under Windows, and then only if you have ANSI.SYS installed (which I don't).
    It is true that they only work for ANSI displays, most of which are modeled on the VT100 and other DEC display terminals. If you're not dealing with ANSI consoles, the "<esc>[2J" that was in the original posting is also useless, so I assume that Ash1981 was working within an ANSI display environment. If I am wrong, I'm sorry.
    Insert obnoxious but pithy remark here

  10. #10
    Registered Luser cwr's Avatar
    Join Date
    Jul 2005
    Location
    Sydney, Australia
    Posts
    869
    Quote Originally Posted by dwks
    BTW, those sequences only work under Windows, and then only if you have ANSI.SYS installed (which I don't).
    They work on any ANSI compatible terminal. This includes many terminals and pseudoterminals created under many unixes including the popular Linux and *BSD family.

  11. #11
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Quote Originally Posted by filker0
    If you read the post, I point out that gets() is unsafe. I never use it myself. Perhaps I could have been a bit clearer.

    quzah's advice to ignore everything I write is annoying. He doesn't think I know what I'm talking about, but then again, he seems to think that nobody knows what they're talking about other than him, so I don't feel singled out.
    Let's examine your post, shall we?

    Quote Originally Posted by filker0
    I don't have the time to wade through all your screen formatting stuff to offer much assistance at the moment, but I will note that mixing getchar() with fgets() is not a good idea; I'd go as far to say that it's a bad idea to mix them.
    Ok, you decide it's a bad idea. Yet, you give no reason for it. First off, getchar is the equivalent to "getc(stdin)". Couple this with their use of fgets ... fgets(ui,2,stdin) ... there is no reason there should ever be a problem on a compiler conforming to the standard. Now you again gave no reason whatsoever. So again, why should they see this as wrong? The fact of the matter is, it's not wrong. It's also not "a bad idea". It's you using a broken implementation. Not them, and definately not the language. It's you and your case, of which you fail to mention anything at all when saying it's "a bad idea".

    Quote Originally Posted by filker0
    fgets() takes a file pointer, getchar() always works on stdin.
    This little gem is completley meaningless to this thread. See, the file pointer they're using is stdin, so it doesn't matter at all. This is a completely pointless statement. It doesn't matter that one function always works on a specific file pointer, because the only file pointer they're using is the one specific one that function just happens to be using.

    Quote Originally Posted by filker0
    If you want fgets() without the f, use gets(), which gets a string from stdin. Note that gets() does not leave the newline ('\n') in the buffer the way that fgets() does, nor does it limit the number of characters read to fit in the buffer, so it's not as safe.
    So here you pretty much tell us that you're only posting to see yourself type. Your whole post is "that's a bad idea, so here's another bad idea you should use instead". You go from one "bad idea", that isn't a bad idea at all, it's simply your broken implementation of the standard, to a "here, this is really stupid, but it's better!" mindset.

    Yet somehow, you can't get past the fact that I don't like your idea. No, it's a damn stupid idea. You post no reason at all why they shouldn't do what they're doing, yet tell them that if they are too lazy to remove the newline from their fgets call, that they should instead use gets, even though it's dangerous.

    Why? Why suggest it at all? Again, you provide no reason why they shouldn't do what they are doing, but suggest using gets even though you say it's dangerous. You're telling them "Hey, your idea is bad; it's even worse than using gets, though I'm not going to even hint at why. Just take my word for it. Oh, and I can't stand it when that one guy says I'm wrong."

    What a maroon.


    Quzah.
    Hope is the first step on the road to disappointment.

  12. #12
    Sr. Software Engineer filker0's Avatar
    Join Date
    Sep 2005
    Location
    West Virginia
    Posts
    235
    Quote Originally Posted by quzah
    Let's examine your post, shall we?
    Let's not. You are ignoring the context in which I made my statements. You only want to show me up as incompetant -- I post to help the person (in this case, Ash1981) asking the questions. I don't post to get points in some imagined game.

    This argument does not belong here in this thread.
    Insert obnoxious but pithy remark here

  13. #13
    Registered Luser cwr's Avatar
    Join Date
    Jul 2005
    Location
    Sydney, Australia
    Posts
    869
    Quote Originally Posted by filker0
    I post to help the person (in this case, Ash1981) asking the questions.
    And I believe quzah's original suggestion to ignore what you said was in order to help the person (in this case, Ash1981) asking the questions.
    Quote Originally Posted by filker0
    You only want to show me up as incompetant
    Regardless of any perceived intentions, pointing out the flaws in a bad answer is inherently valuable. I certainly appreciate it when someone points out my mistakes.

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