0

Let us suppose the following code:

#include <stdio.h>
int main()
{
   int n;
   scanf("%d",&n);
}

How can I ensure that the data provided by the user is an integer (and not, let's say, a char)?

Is there any library for reading data from the user that ensures that?

Zaratruta
  • 2,097
  • 2
  • 20
  • 26
  • 3
    Check the return value of `scanf`? Or read in the user input as a string and then try to convert it (with error checking) – UnholySheep Apr 26 '19 at 17:05
  • https://stackoverflow.com/questions/865284/what-is-the-easiest-way-to-get-an-int-in-a-console-app (answer by dwc) – Attersson Apr 26 '19 at 17:05
  • Usually you don't care about the input format, you care about *valid inputs*. If `n` has to be between 1 and *N* then you test that, not if it contains characters. *Garbage in, garbage out* helps keep your code simple. – tadman Apr 26 '19 at 17:21
  • By "ensure", do you mean removing the user's ability to enter non-digits in the first place? That's not within a program's power. – John Bollinger Apr 26 '19 at 17:43
  • @JohnBollinger...By "ensure" I mean being able to identify when the user doesn't provide an integer value in the example and being able to deal with this situation in a suitable way. – Zaratruta Apr 29 '19 at 16:58

1 Answers1

0

There's additional work you have to do around the scanf call.

First of all, scanf will return the number of successful conversions and assignments - in this case, you would expect 1 on success. If there are no successful conversions and assignments, it will return 0. If there's an error on input, it will return EOF.

So, as a first pass, you'd do something like this:

if ( scanf( "%d", &n ) != 1 )
  // input was not an integer or error on input
else
  // successful input, do something with n

By itself, this is not enough.

%d tells scanf to skip over leading whitespace, then read up to the first non-digit character. If you enter "abc", then there are no valid digit characters, scanf returns 0, and n is unmodified.

However, if you enter a string like "12c", then "12" will be read and converted, n will be assigned the value 12, scanf will return 1, and 'c' will be left in the input stream to foul up the next read. Ideally, you should reject that whole input.

There are a couple of ways around this. You can peek at the character immediately following your input - if it's whitespace, then the input was valid:

char dummy;
int items_read = scanf( "%d%c", &n, &dummy );

If items_read is 2, that means scanf read at least one decimal digit character followed by a non decimal digit character. If dummy contains a whitespace character, that means the input was a valid integer. If dummy contains a non-whitespace character, then the user fatfingered a nondigit character on input (meaning the value in n shouldn't be trusted). Either way, we need to push that dummy character back onto the input stream before continuing.

If items_read is 1, that means we read at least one decimal digit character followed by hitting EOF, so that means our input has to be a valid integer as well.

So, the test would look something like this:

if ( items_read == 2 && isspace( dummy ) || items_read == 1 )
{
  // do something with n
  if ( items_read == 2 )
    ungetc( dummy, stdin );
}
else
{
  // input was not an integer, or error on input
}

Alternately, you can read your input as a string, then use strtol to do the conversion:

char input[MAX_INPUT_LENGTH+1];
if ( fgets( input, sizeof input, stdin ) ) // using fgets instead of scanf for this
{
  char *chk; // stores address of first character *not* converted
  long tmp = strtol( input, &chk, 0 ); // convert into a temporary
  if ( isspace( *chk ) || *chk == 0 )
  {
    // input is good, assign tmp to n
    if ( tmp <= INT_MAX && tmp >= INT_MIN )
      n = tmp;
    else
      // input was within range for long but not int
  }
  else
    // trailing non-digit character
}
John Bode
  • 119,563
  • 19
  • 122
  • 198