简体   繁体   中英

Input validation in C only Integer

My program is the Mario pyramids. I can't seem to get the Input Validation in C down. If someone could explain input validation and what seems to be going wrong. Thanks. Here is a copy of my code.

// Prompt user till input is 0-23
do
{
    printf("Welcome to Mario Pyramid Builder! Enter a number from 0 - 23: ");
    scanf("%d", &height);
    if(height >= 0 && height <= 23)
    {
        break;
    }
    else
    {
        printf("Wrong Input! Try Again.");
        continue;
    }
}
while ((height < 0) || (height > 23));

scanf does not remove extra characters it only extracts what you put in the format specifier, the other characters like \\n remain in the buffer. To avoid scanf complications use fgets instead to read a line from keyboard, then use sscanf() to extract the integer (or just plain old atoi() )

...
char buffer[128];
if (fgets( buffer, sizeof(buffer), stdin) != NULL)
{
  if (sscanf(buffer, "%d", &height) == 1) 
  {
    if (height >= 0 && height <= 23)
    {
      ...
    }
    else
    {
      fprintf(stderr, "Height outside valid range [0,23]\n");
    }
  }
  else
  {
    fprintf(stderr, "Please enter a numeric Height in range [0,23]\n");
  }
}
...

Input validation in C is a pain in the ass , and requires a bit of work on your part. Do not rely on scanf to do the hard work for you, because it won't. If you enter an input like 12dw , scanf with the %d conversion specifier will successfully read, convert, and assign 12 to your target while leaving dw in the input stream to foul up the next read.

Ideally for interactive input, you should read your input into a text buffer and convert to the target type yourself. It will help protect against cases like the above, and it will help in case there's an obnoxiously long input that's attempting an overflow exploit or something like that.

So, start with a fixed-size array of char for your input buffer:

#define MAX_INPUT_LENGTH 13     // enough for 32-bit decimal integer, plus sign
char input[MAX_INPUT_LENGTH+1]; // +1 for string terminator

You'll use fgets to read the input:

if ( !fgets( input, sizeof input, stdin ) )
{
  // error on input
}
else
{
  // process input
}

The next check you'll want to make is to see if there's a newline in the input buffer - if not, then your user entered a value that's too large to represent as a native integer type. If that's the case, you'll want to discard the current input, then read and discard everything up to and including the next newline:

char *newline = strchr( input, '\n' );
if ( !newline )
{
  while ( getchar() != '\n' ) // read and discard everything up to the next newline
    ;
}
else
{
  // convert input
}

Now you're ready to convert your input from text to integer. Use the strtol library function for this (include stdlib.h for its prototype), as it will allow to check if there are any non-numeric, non-whitespace characters in the input.

char *chk;
height = (int) strtol( input, &chk, 0 );
if ( !isspace( *chk ) || *chk != 0 ) // include ctype.h for isspace
{
  // conversion was unsuccessful, bad input
}
else
{
  // check range
}

Putting all of that together, you get something like this:

#include <stdlib.h>
#include <ctype.h>
...
#define MAX_INPUT_LENGTH 13
...
int height;
int done = 0;

printf( "Welcome to Mario Pyramid Builder!\n" );
do
{
  char input[MAX_INPUT_LENGTH+1];
  height = -1;                        // initialize height to an invalid value
  printf( "Enter a number from 0 to 23: " );
  if ( !fgets( input, sizeof input, stdin ) )
  {
    fprintf( stderr, "Error on input, try again!\n" );
  }
  else
  {
    //
    // make sure input wasn't too long for the input buffer by 
    // checking for a newline.
    //
    char *newline = strchr( input, '\n' );
    if ( !*newline )
    {
      while ( getchar() != '\n' )
        ;
      fprintf( stderr, "Input too long, try again!\n" );
    }
    else
    {
      //
      // Convert text in input buffer to an integer.  chk will point
      // to the first character in input that is not a decimal digit.
      // If that character is not 0 or whitespace, then we have
      // bad (non-numeric) input.
      //
      char *chk;
      height = strtol( input, &chk, 10 );
      if ( !isspace( *chk ) || *chk != 0 )
      {
        fprintf( stderr, "Non-numeric input detected, try again!\n" );
      }
      else
      {
        // 
        // if height is between 0 and 23, done will be set to true,
        // and we'll exit the loop.
        //
        done = height >= 0 && height <= 23;
      }
    }
  }
} while ( !done );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM