Search code examples
cloopsif-statementinputverification

C - Input Verification in loop acting weird


I am writing an input routine that requests a number between 1 and 30 from the user, and which is meant to emit an error message and re-prompt the user for input until valid input is received. I used a loop to read the input and check it against various cases, exiting the loop once appropriate input is received.

The case checking against whether the number is in range works (or did work) perfectly fine, but when I check to make sure that the user did not enter anything non-numeric, the whole thing pretty much blows up in my face, and the loop seems to act weirdly. I was told at first it was an issue with not closing the input buffer so I added that fix, but then despite the condition being met the first time, it would not jump back to the front of the loop to allow the input to be altered -- instead it would immediately jump to a branch and continually print that message on the order or thousands of times per second.

Below is the code. The part that gets continually printed is the if( isalpha(uNum) ){.... For this revision of my code, putting inputs above 30 and below 1 work just fine, but throwing in something like "abc" continually prints the message for going over 30 (i.e. "Value must be at most 30..."). When I take out the statement to clear the buffer ( while(getchar() != '\n'); ) the aforementioned messages are constantly spammed without end. Can somebody help me out with why that is and/or what I need to do to fix it?

int get_seed(void)
{
    int uNum;
    printf("Please enter an integer between 1 and 30:\n");
    while(1) {
        scanf("%d",&uNum);
        while(getchar() != '\n');
        if( isalpha(uNum) ){
            printf("Your entry must start with a digit; try again\n");
        }
        else if( (uNum < 1) || (uNum > 30) ){
            if(uNum<1){
                printf("Value must be at least one; try again\n");
            } else {
                printf("Value must be at most 30; try again\n");
            }
            continue;
        }
        else
            break;
    }
    return uNum;
}

At this point I am wondering if it would make more sense to simply change the "else break;" statement (which means the input was good) to have a compound condition of <=30 and >=1 so that a new else statement takes care of what the first if was supposed to do.


Solution

  • scanf can be a bit tricky when attempting to get input within a loop due to the potential for an infinite loop if you fail to empty stdin between calls to scanf. This isn't perfect, but it is a lot closer to what you are looking for. The key is to make use of the return to scanf to determine whether a matching failure occurred:

    #include <stdio.h>
    
    #define MAXNUM 30
    
    int main () {
    
        int uNum = 0;
    
        printf ("\nEnter a number between 1 and %d:\n", MAXNUM);
    
        while (printf ("\n  uNum: "))
        {
            int rtn = scanf ("%d", &uNum);
            int c = 0;
    
            /* check for matching failure, empty input buffer */
            if (rtn == 0) do { c = getchar(); } while ( c != '\n' && c != EOF);
    
            if (uNum > 0 && uNum < MAXNUM + 1) break;
        }
    
        printf ("\nValue : %d\n\n", uNum);
    
        return 0;
    
    }
    

    Output

    $ ./bin/scf130
    
    Enter a number between 1 and 30:
    
      uNum: a
    
      uNum: -1
    
      uNum: 31
    
      uNum: 18
    
    Value : 18
    

    Note: this will accept numbers 1 ... 30, if you want 2 ... 29 simply adjust the tests.