Laith
Laith

Reputation: 81

How can I validate scanf numeric input

I have a simple question. I'm learning linked lists and my data type is int. I'm wondering how can validate it where it doesn't allow to take in alphabetic input.

When I do it the way I have it, it doesn't print the message. So how can I properly do something like that?

My code:

node * createLinkedList()
{
    node * head = NULL;
    node * temp = NULL;
    node * p = NULL;
    
    while ((scanf("%d", &(temp->data))) !=EOF )
    {
        if ((temp->data>= 'a' && temp->data<= 'z') || (temp->data>= 'A' && temp->data<= 'Z')){
            fprintf(stderr,"ERROR alph is not allowed");
        }

    }
}

Upvotes: 1

Views: 147

Answers (2)

John Bode
John Bode

Reputation: 123448

If you need to validate integer input, then you can't rely on %d alone. It won't catch and reject bad entries like "12w45" - it will successfully convert and assign "12" but will leave "w45" in the input stream to foul up the next read.

There are two ways around this. One is to scan and check the character immediately following the input, like so:

int tmp;
char dummy = 0;

int r;

if ( (r = scanf( "%d%c", &tmp, &dummy )) == 2 )
{
  // if following character is whitespace, then this is a valid numeric input
  if ( isspace( dummy ) )
    temp->data = tmp;
  else
  {
    fprintf( stderr, "non-numeric character '%c' (%d) detected in input\n",
      isprint( dummy ) ? dummy : '.', dummy );

    fprintf( stderr, "clearing out input stream\n" );
    while ( getchar() != '\n' )
      ; // empty loop
  }
}
else if ( r == 1 ) // only thing following numeric input was EOF
{
  temp->data = tmp;
}
else if ( r == 0 )
{
  fprintf( stderr, "Non-numeric input detected, clearing input stream\n" );
  while ( getchar() != '\n' )
    ; // empty loop
}
else
{
  fprintf( stderr, "EOF or error detected on input\n" );
}

A better way to do this in my opinion is to avoid using scanf entirely - read your input as a string using fgets, then use strtol or strtod to perform the conversion:

char buffer[13]; // 11 decimal digits plus sign plus string terminator;
                 // should be able to store the decimal string representation
                 // of any 32-bit integer;

if ( fgets( buffer, sizeof buffer, stdin ) )
{
  // chk will point to the first character *not* converted by strtol
  char *chk;
  int tmp = (int) strtol( buffer, &chk, 10 );
  if ( !isspace( *chk ) && *chk != 0 )
  {
    fprintf( stderr, "Detected non-numeric character '%c' (%d) in input\n",
      isprint( *chk ) ? *chk : '.', *chk );
  }
  else
  {
    temp->data = tmp;
  }
}
else
{
  fprintf( stderr, "EOF or error on input\n" );
}

When you're doing this kind of validation, use a temporary to store the converted value until you've finished; do not update your actual target until you know the value is good.

Upvotes: 2

anastaciu
anastaciu

Reputation: 23792

The %d scanf specifier is to read int, if an alphabetic character is inputed, scanf will fail to read it and temp->data will retain its previous value.

A better condition for your cycle would be:

while (scanf("%d", &(temp->data)) == 0) //while input is not correctly parsed...
{
    fprintf(stderr, "ERROR alph is not allowed\n"); //...print error message...
    int c;
    while((c = getchar()) != '\n' && c != EOF) {} //...and clear stdin
}

This makes it so that if the input is not parseable as an int a new input will be asked until a good value is inputed.

Note that, as @JohnBode pointed out, an input starting with a digit but that contains alphabetic characters will be parsed until the character is found, i.e. 123abc4 will parse 123. If you want to avoid this, using a combination of fgets and strtoll is a more robust method and will allow you to validate inputs like the above.

If it's something that you want to allow to happen, and continue using scanf, don't forget you need to then clear stdin which will have the characters that were not parsed, (in the exemplified case abc4\n), before asking for new inputs, @JohnBode's answer covers these matters perfectly, check that out.


There is another problem in the code you show, your temp pointer is NULL, it is not able to store any data. Either declare it as an object or allocate memory for it. As it is, it leads to undefined behavior.


Upvotes: 3

Related Questions