Search code examples
cuser-input

How to detect non-integer input in C


I have this program:

#include <stdio.h>

int main(){
    int val;
    printf("Enter any number: ");
    scanf("%d",&val);
    printf("The number incremented is %d\n",val+1);
    printf("Enter any number again: ");
    scanf("%d",&val);
    printf("The number decremented is: %d\n",val-1);
    return 0;
}

It works completely fine if i give it an integer input, but when its a different input, the program goes crazy and runs without accepting the second input. (the second output is -1). is there a fix to this?

I have tried to test the program with number and non-number inputs, For the non number one in the first input, as the description said, the program continued and didnt accept the second input.

Edit: I did not mention the desired output of the program, sorry about that. If the output is non-integer then it will return with err code 1.


Solution

  • If you want the function to return -1; in the case of the user entering invalid input, then you should check the return value of scanf, for example like this:

    #include <stdio.h>
    
    int main( void )
    {
        int val;
    
        printf( "Enter any number: ");
        if ( scanf( "%d", &val ) != 1 )
        {
            printf( "Invalid input!\n" );
            return -1;
        }
    
        printf( "The number incremented is %d\n", val+1 );
    
        printf( "Enter any number again: " );
        if ( scanf( "%d", &val ) != 1 )
        {
            printf( "Invalid input!\n" );
            return -1;
        }
    
        printf( "The number decremented is: %d\n", val-1 );
    
        return 0;
    }
    

    This program has the following behavior:

    Enter any number: abc
    Invalid input!
    
    Enter any number: 5
    The number incremented is 6
    Enter any number again: abc
    Invalid input!
    
    Enter any number: 5
    The number incremented is 6
    Enter any number again: 10
    The number decremented is: 9
    

    However, this solution is not perfect. For example, if the user enters 5abc in the first line, then the first scanf successfully reads 5, but the second scanf will fail:

    Enter any number: 5abc
    The number incremented is 6
    Enter any number again: Invalid input!
    

    If you don't want this counter-intuitive behavior, then it would probably be best not to use the function scanf for line-based user input, but to rather use the functions fgets and strtol instead, for example like this:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    #include <limits.h>
    #include <errno.h>
    
    int get_int_from_user( const char *prompt );
    
    int main( void )
    {
        int val;
    
        val = get_int_from_user( "Enter any number: ");
        printf( "The number incremented is %d\n", val+1 );
    
        val = get_int_from_user( "Enter any number again: ");
        printf( "The number decremented is: %d\n", val-1 );
    
        return 0;
    }
    
    int get_int_from_user( const char *prompt )
    {
        //loop forever until user enters a valid number
        for (;;)
        {
            char buffer[1024], *p;
            long l;
    
            //prompt user for input
            fputs( prompt, stdout );
    
            //get one line of input from input stream
            if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
            {
                fprintf( stderr, "Unrecoverable input error!\n" );
                exit( EXIT_FAILURE );
            }
    
            //make sure that entire line was read in (i.e. that
            //the buffer was not too small)
            if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
            {
                int c;
    
                printf( "Line input was too long!\n" );
    
                //discard remainder of line
                do
                {
                    c = getchar();
    
                    if ( c == EOF )
                    {
                        fprintf( stderr, "Unrecoverable error reading from input!\n" );
                        exit( EXIT_FAILURE );
                    }
    
                } while ( c != '\n' );
    
                continue;
            }
    
            //attempt to convert string to number
            errno = 0;
            l = strtol( buffer, &p, 10 );
            if ( p == buffer )
            {
                printf( "Error converting string to number!\n" );
                continue;
            }
    
            //make sure that number is representable as an "int"
            if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
            {
                printf( "Number out of range error!\n" );
                continue;
            }
    
            //make sure that remainder of line contains only whitespace,
            //so that input such as "6sdfj23jlj" gets rejected
            for ( ; *p != '\0'; p++ )
            {
                if ( !isspace( (unsigned char)*p ) )
                {
                    printf( "Unexpected input encountered!\n" );
    
                    //cannot use `continue` here, because that would go to
                    //the next iteration of the innermost loop, but we
                    //want to go to the next iteration of the outer loop
                    goto continue_outer_loop;
                }
            }
    
            return l;
    
        continue_outer_loop:
            continue;
        }
    }
    

    This program has the following behavior:

    Enter any number: 5abc
    Unexpected input encountered!
    Enter any number: abc
    Error converting string to number!
    Enter any number: 5
    The number incremented is 6
    Enter any number again: 10
    The number decremented is: 9
    

    Note that my second program is more sophisticated than the first program, because it keeps on prompting the user for input until the user enters a valid int, whereas the first program simply prints an error message and returns -1.

    I took the function get_int_from_user from this answer of mine to another question. See that answer for more information on the extensive input validation that the function performs.