Thread: Number user input

  1. #1
    Registered User jimtuv's Avatar
    Join Date
    May 2010
    Location
    Sylvania, Ohio
    Posts
    94

    Number user input

    I was going over number input in the FAQ here. I am about half done reading it and wanted to try out some of the early examples both good and bad to understand what is exactly going on. Here is a function I made. I added a min and max to limit the size of the number.

    The call looks like this:
    Code:
       printf("Enter a number (0-20)\n");
        number = UserInput(MIN_NUMBER, MAX_NUMBER);

    Code:
    int UserInput(int min, int max){
    
        int input;    
        input = 0;
    
        printf(CURSOR);
    
        while (scanf("%d",&input) != 1 || input < min || input > max){
            while (getchar() != '\n');
            printf(CURSOR);
        }
    
        // while (getchar() != '\n'); <<<--- Should I put this here??
    
        return input;
    }

    My question is two fold. First, shouldn't I clear the stdin before leaving this function. From what I can tell it still leaves junk in the buffer.

    Two what would happen if the user exceeds the buffer? I tried it and seems the function works fine no matter how large a number is entered.

  2. #2
    Registered User jimtuv's Avatar
    Join Date
    May 2010
    Location
    Sylvania, Ohio
    Posts
    94
    I have made a test code to tell what is left in the buffer

    Code:
    #include <stdio.h>
    
    
    #define MIN_NUMBER 0
    #define MAX_NUMBER 20
    #define CURSOR ">>>"
    
    
    int UserInput(int Min, int Max);
    
    
    int main(void){
        
    	
        int number;
        
        char name [BUFSIZ];
    
            
        
        printf("Enter a number (0-20)\n");
        number = UserInput(MIN_NUMBER, MAX_NUMBER);
        printf("this is outside the function\n");
        fgets(name, BUFSIZ, stdin);
    
        
        printf("number you entered = %d\n",number);
        printf("This is junk left %s",name);
    
    
    	
        return 0;
    }
    
    int UserInput(int min, int max){
    
        int input;    
        input = 0;
    
        printf(CURSOR);
    
        while (scanf("%d",&input) != 1 || input < min || input > max){
            while (getchar() != '\n');
            printf(CURSOR);
        }
    
        //while (getchar() != '\n');
        
        return input;
    }
    The output looks like this

    Enter a number (0-20)
    >>>12 this is junk left in the buffer
    this is outside the function
    number you entered = 12
    This is junk left this is junk left in the buffer
    Press [Enter] to close the terminal ...

    I do not get a prompt for more input it just goes right to the print.

    Now I uncomment that while(getchar() != '\n'); and this is the output

    Enter a number (0-20)
    >>>12 this is junk left in the buffer
    this is outside the function
    no junk this time
    number you entered = 12
    This is junk left no junk this time
    Press [Enter] to close the terminal ...

    Now I do get a prompt for input it does not go straight to the print.
    It waits for input at the fgets(name, BUFSIZ, stdin);


    As you can see it does leave stuff in the buffer so I guess I answered that question. But I am still unclear what happens in a buffer overflow.
    Last edited by jimtuv; 06-05-2010 at 05:40 PM.

  3. #3
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    It's not buffer overflow, that's something else entirely. If I ask you to enter a number on your keyboard via some console application, as you are doing here, what really happens?

    1<enter>

    That's two keystrokes. You read the number, and the <enter> is hanging around waiting for you to do something with it. Different methods of reading handle the buffer differently. Some of them are non-buffered, like this edit box in the browser for example, and as soon as you hit a key, it sees I just pressed d and puts a d on my screen. Some of them are line buffered, meaning it waits until a specific event happens, before it reads the input. This event is typically the newline character (enter) being hit, hence the term 'line buffered'.

    So basically, you just need to remember that most every keystroke you hit (for the most part, ALT, CTRL, etc., aren't generally registered the same way) needs to be handled appropriately. I.e., you hit enter, so you need to do something to catch that enter key that you've entered, and do something with it. A call to getchar, simply reads the next available key that is in the buffer, and returns it (which here is just ignored).


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

  4. #4
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    You cannot get a buffer overflow because your code is safe. What happens if the user types more characters than you have room for is simply that fgets returns having read BUFISZ characters and leaves the rest in the buffer. Next time you read, it fetches BUFSIZ more, and so on.
    Now, if you changed that constant not to match the true buffer size, you may get a buffer overflow. If you're curious about what a buffer overflow can do, look no further than SourceForge.net: Buffer overrun - cpwiki
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  5. #5
    Registered User jimtuv's Avatar
    Join Date
    May 2010
    Location
    Sylvania, Ohio
    Posts
    94
    Thank you so much Elysia and quzah! The info is helping me understand whats happening. I sure do appreciate the people who contributed to the FAQ as well. It is an awesome tool! I have learned so much from it. I still only understand the basic information there as I am just starting to learn. That's why I have been exploring the example code given there. It sure helps to reproduce it and play with the code to see what everything does.

    Input is a very complex subject. I can see it will take a while to learn all the intricacies. I can see why the tutorials just assume the user will input the correct data (not that I agree with the practice). Trying to bullet proof user input takes a deeper understanding then a beginner is capable of. Though it seems it would be a good idea when teaching to show the correct way to code user input and just state that it needs to be done this way and an explanation will come later when it will be understood. Instead of just assuming that users will input flawlessly.

    My plans are to understand and build up from the least complex

    single digit integer input
    multi digit integer input
    floating point numbers
    alpha characters to strings
    mix number and strings

    I hope in that way I can get a feel for the complexities one step at a time.

    I had been avoiding trying out the code that was considered the wrong way to do it. But I have found it informative to test out bad code and see why it fails. I have actually learned more from this approach then just memorizing and testing the recommended code.

  6. #6
    Registered User jimtuv's Avatar
    Join Date
    May 2010
    Location
    Sylvania, Ohio
    Posts
    94
    Ok I went through and made a new example of a integer number input using a custom function which is modified from the last FAQ example code. I added it to the bottom of my other examples to compare them. It is interesting that the custom function is the only one of the three that does not fail in some part. I see now how much better it is to use fgets into a string because of the ability to check the entire string for correctness before converting the string into a int. The great part is I don't have to worry about junk in the stdin left over after the call. The only drawback I have right now is if the user hits the enter key without typing anything it returns 0 instead of calling that an error and waiting for more input.

    Here is my code with output at the end

    Code:
    /* 
     * File:   numbinput.c
     * Author: jim
     *
     * Created on June 5, 2010, 11:50 AM
     */
    
    /* *******************************************************
    * Testbed for number input routines
    *************************************************************/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <string.h>
    
    #define MIN_NUMBER 0
    #define MAX_NUMBER 20
    #define MIN_FLOAT 0.01
    #define MAX_FLOAT 20.00
    #define MIN_SNUMBER 0
    #define MAX_SNUMBER 50
    #define CURSOR ">>>"
    
    int UserInput(int min, int max);
    double UserInputFloat(double min, double max);
    int UserInputStrgNumb(int min, int max);
    int Validate(char *a);
    
    int main(void){
        
    	
        int number;
        double fnumber;
        int snumber;        
        
        printf("Enter a number for UserInput (0-20)\n");
        number = UserInput(MIN_NUMBER, MAX_NUMBER);
    
        printf("Enter number for UserInputFloat (0.00-20.00)\n");
        fnumber = UserInputFloat(MIN_FLOAT, MAX_FLOAT);
    
        printf("Enter a number for UserInputStrgNumb (0-50)\n");
        snumber = UserInputStrgNumb(MIN_SNUMBER, MAX_SNUMBER);   
            
        printf("The number you entered for UserInput = %d\n",number);
        printf("The number you entered for UserInputFloat = %.2f\n",fnumber);
        printf("The number you entered for UserInputStrgNumb = %d\n",snumber);
    	
        return 0;
    }
    /***********************************
     * UserInput
     * Parameters:
     * min:         minimum number to except
     * max:         maximum number to except
     * Returns:     integer number in the range (min,max)
     * Prints:      nothing
     ***********************************/
    int UserInput(int min, int max){
    
        int input;   
        input = 0;
    
        printf(CURSOR);
        
        while (scanf("%d",&input) != 1 || input < min || input > max){
            while (getchar() != '\n'); 
            printf(CURSOR);
        }
    
        while (getchar() != '\n'); 
                                    
        
        return input;
    }
    /***********************************
     * UserInputFloat
     * Parameters:
     * min:         minimum number to except
     * max:         maximum number to except
     * Returns:     double floating point number in the range (min,max)
     * Prints:      nothing
     ***********************************/
    double UserInputFloat(double min, double max){
    
        double input;
        input = 0.0;
    
        printf(CURSOR);
    
        while (scanf("%lf",&input) != 1 || input < min || input > max ){
            while (getchar() != '\n');
            printf(CURSOR);
        }
    
        while (getchar() != '\n');
    
        return input;
    }
    /***********************************
     * UserInputStrgNumb
     * Parameters:
     * min:         minimum number to except
     * max:         maximum number to except
     * Returns:     integer number in the range (min,max)
     * Prints:      nothing
     ***********************************/
    int UserInputStrgNumb(int min, int max){
    
        int i;
        int error = 1;
        char buffer[BUFSIZ]; 
    
        printf(CURSOR);
    
        while(error == 1 || error == 2 || i < min || i > max){
            if (fgets(buffer, sizeof buffer, stdin) != NULL) {
                 buffer[strlen(buffer) - 1 ] = '\0';
                 if (Validate(buffer) == 0){
                     i = atoi(buffer);
                     error = 0;
                     if ( i < min || i > max){
                         printf(CURSOR);
                     }
                 }
                 else{
                     error = 2;
                     printf(CURSOR);
                 }
            }
            else{
                error = 1;
                printf(CURSOR);
            }
        }
        return i;
    }
    /***********************************
     * Validate
     * Parameters:
     * char *a:     is a pointer to the string containing the possible number
     * Returns:     int error code (o for good number and 1 for bad number)
     * Prints:      nothing
     ***********************************/
    
    int Validate(char *a){
    
        unsigned x;
        
        for (x = 0; x < strlen ( a ); x++){
            if (!isdigit(a[x])) return 1;
        }
        return 0;
    }
    
    /*
     *  OUTPUT
    
    Enter a number for UserInput (0-20)
    >>>a
    >>>a1
    >>>-1
    >>>22
    >>>12d
    Enter number for UserInputFloat (0.00-20.00)
    >>>-1.0
    >>>0.003
    >>>21.5
    >>>a12.5
    >>>1a2.5
    Enter a number for UserInputStrgNumb (0-50)
    >>>-1
    >>>51
    >>>a51
    >>>5a1
    >>>51a
    >>>25a
    >>>a25
    >>>2a5
    >>>25
    The number you entered for UserInput = 12
    The number you entered for UserInputFloat = 1.00
    The number you entered for UserInputStrgNumb = 25
    Press [Enter] to close the terminal ...
    
     */

  7. #7
    Registered User jimtuv's Avatar
    Join Date
    May 2010
    Location
    Sylvania, Ohio
    Posts
    94
    fixed the UserInputStrgNumber functions it was as simple as adding a check for '\0' at the beginning of the buffer array and sticking in another printf(CURSOR);

    so here is the fixed code:
    Code:
    int UserInputStrgNumb(int min, int max){
    
        int i;
        int error = 1;
        char buffer[BUFSIZ]; 
    
        printf(CURSOR);
    
        while(error == 1 || error == 2 || i < min || i > max || buffer[0] == '\0'){
            if (fgets(buffer, sizeof buffer, stdin) != NULL) {
                 buffer[strlen(buffer) - 1 ] = '\0';
                 if (Validate(buffer) == 0){
                     i = atoi(buffer);
                     error = 0;
                     if ( i < min || i > max){
                         printf(CURSOR);
                     }
                 }
                 else{
                     error = 2;
                     printf(CURSOR);
                 }
            }
            else{
                error = 1;
                printf(CURSOR);
            }
            
          printf(CURSOR);
        }
       return i;
    }

  8. #8
    Registered User
    Join Date
    May 2010
    Location
    Naypyidaw
    Posts
    1,314
    UserInputStrgNumb() is not accepting negative numbers.

  9. #9
    Registered User jimtuv's Avatar
    Join Date
    May 2010
    Location
    Sylvania, Ohio
    Posts
    94
    Quote Originally Posted by Bayint Naung View Post
    UserInputStrgNumb() is not accepting negative numbers.
    Yeah haven't gotten that far with it yet. Been considering just menus so negatives were not necessary. I will have to play with it to see how to do that.


    my first thought would be to check buffer [0] for the - sign and remove it if it's there then treat the rest as a normal integer.
    Last edited by jimtuv; 06-07-2010 at 07:23 AM.

  10. #10
    Registered User jimtuv's Avatar
    Join Date
    May 2010
    Location
    Sylvania, Ohio
    Posts
    94
    Not exactly the prettiest way to handle negative number it but it seems to work OK. Of course you will have to make sure the min is set in the negative range or the condition of the while loop will keep it from occurring.


    Code:
    int UserInputStrgNumb(int min, int max){
    
        int i;
        int error;
        int sign;
        char buffer[BUFSIZ];
    
        error = 1;
        sign = 0;
    
        printf(CURSOR);
    
        while(error == 1 || error == 2 || i < min || i > max || buffer[0] == '\0'){
            if (fgets(buffer, sizeof buffer, stdin) != NULL) {
                 buffer[strlen(buffer) - 1 ] = '\0';
                 if (buffer[0] == '-'){
                     sign = 1;
                     buffer[0] = '0';
                 }
                 if (Validate(buffer) == 0){
                     i = atoi(buffer);
                     if ( sign == 1){
                         i = i * -1;
                     }
                     error = 0;
                     if ( i < min || i > max){
                         printf(CURSOR);
                     }
                 }
                 else{
                     error = 2;
                     printf(CURSOR);
                 }
            }
            else{
                error = 1;
                printf(CURSOR);
            }
            
          printf(CURSOR);
        }
       return i;
    }

  11. #11
    Registered User
    Join Date
    May 2010
    Location
    Naypyidaw
    Posts
    1,314
    Just use fgets() + strtol().

  12. #12
    Registered User jimtuv's Avatar
    Join Date
    May 2010
    Location
    Sylvania, Ohio
    Posts
    94
    Quote Originally Posted by Bayint Naung View Post
    Just use fgets() + strtol().
    Going to have to look into that. strtol looks kind complex. So how would I handle the Validation?? Would it be just pretty much the same as I have except checking the beginning for the sign??

    Like this

    Code:
    int Validate(char *a){
    
        unsigned x;
    
        if (a[0] != '-' && a[0] != '+'){
            for (x = 0; x < strlen ( a ); x++){
            if (!isdigit(a[x])) return 1;
            }
        }
        else{
            for (x = 1; x < strlen ( a ); x++){
            if (!isdigit(a[x])) return 1;
            }
        }
        return 0;   
    }
    Last edited by jimtuv; 06-07-2010 at 09:20 AM.

  13. #13
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    strtol is not complex. It's very easy, actually.
    Code:
    const char* number = "-100";
    const char* pEnd = NULL;
    long l = strtol(number, &pEnd, 10);
    if (pEnd == number)
        ; // Operation failed; number does not contain a number.
    If it succeeds, l contains your number.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  14. #14
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    If it doesn't errno should report what happened as well.

  15. #15
    Registered User jimtuv's Avatar
    Join Date
    May 2010
    Location
    Sylvania, Ohio
    Posts
    94
    So using atoi and validating the input using isdigit the way I did is a mistake??? I can see that strtol is great for conversions from other bases but what is the advantage here?? And how do I handle the validation.

    I am totally unfirmilliar with errno though if I remember I read something about that a few days ago. I would have to #include <errno.h> and then on a failure return code from strol check the value that is in errno for what type of error it was correct? This would be only necessary if I were going to do something specific after an error. Otherwise I could just force reentry of the data on an error from strol and then checking errno would be unnecessary.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. How to limit user input to a certain number of digits?
    By NewbGuy in forum C Programming
    Replies: 7
    Last Post: 05-08-2009, 09:57 PM
  2. Program that requests input of an integer number
    By theejuice in forum C Programming
    Replies: 6
    Last Post: 04-30-2008, 02:18 AM
  3. Input statement problem
    By une in forum C Programming
    Replies: 3
    Last Post: 05-29-2007, 11:16 PM
  4. Prevent user input
    By bonne_tim in forum C Programming
    Replies: 11
    Last Post: 01-01-2007, 03:18 PM
  5. ~ User Input script help~
    By indy in forum C Programming
    Replies: 4
    Last Post: 12-02-2003, 06:01 AM