[c] How to scanf only integer?

I want the code to run until the user enters an integer value.

The code works for char and char arrays.

I have done the following:


#include<stdio.h>
int main()
{
    int n;
    printf("Please enter an integer: ");
    while(scanf("%d",&n) != 1)
    {
        printf("Please enter an integer: ");
        while(getchar() != '\n');
    }
    printf("You entered: %d\n",n);
    return 0;
}

The problem is if the user inputs a float value scanf will accept it.

Please enter an integer: abcd
Please enter an integer: a
Please enter an integer: 5.9
You entered: 5

How can that be avoided?

This question is related to c scanf

The answer is


If you're set on using scanf, you can do something like the following:

int val;
char follow;  
int read = scanf( "%d%c", &val, &follow );

if ( read == 2 )
{
  if ( isspace( follow ) )
  {
    // input is an integer followed by whitespace, accept
  }
  else
  {
    // input is an integer followed by non-whitespace, reject
  }
}
else if ( read == 1 )
{
  // input is an integer followed by EOF, accept
}
else
{
  // input is not an integer, reject
}

I know how this can be done using fgets and strtol, I would like to know how this can be done using scanf() (if possible).

As the other answers say, scanf isn't really suitable for this, fgets and strtol is an alternative (though fgets has the drawback that it's hard to detect a 0-byte in the input and impossible to tell what has been input after a 0-byte, if any).

For sake of completeness (and assuming valid input is an integer followed by a newline):

while(scanf("%d%1[\n]", &n, (char [2]){ 0 }) < 2)

Alternatively, use %n before and after %*1[\n] with assignment-suppression. Note, however (from the Debian manpage):

This is not a conversion, although it can be suppressed with the * assignment-suppression character. The C standard says: "Execution of a %n directive does not increment the assignment count returned at the completion of execution" but the Corrigendum seems to contradict this. Probably it is wise not to make any assumptions on the effect of %n conversions on the return value.


A possible solution is to think about it backwards: Accept a float as input and reject the input if the float is not an integer:

int n;
float f;
printf("Please enter an integer: ");
while(scanf("%f",&f)!=1 || (int)f != f)
{
    ...
}
n = f;

Though this does allow the user to enter something like 12.0, or 12e0, etc.


Use fgets and strtol,

A pointer to the first character following the integer representation in s is stored in the object pointed by p, if *p is different to \n then you have a bad input.

#include <stdio.h>
#include <stdlib.h>

int main(void) 
{
    char *p, s[100];
    long n;

    while (fgets(s, sizeof(s), stdin)) {
        n = strtol(s, &p, 10);
        if (p == s || *p != '\n') {
            printf("Please enter an integer: ");
        } else break;
    }
    printf("You entered: %ld\n", n);
    return 0;
}

  1. You take scanf().
  2. You throw it in the bin.
  3. You use fgets() to get an entire line.
  4. You use strtol() to parse the line as an integer, checking if it consumed the entire line.
char *end;
char buf[LINE_MAX];

do {
     if (!fgets(buf, sizeof buf, stdin))
        break;

     // remove \n
     buf[strlen(buf) - 1] = 0;

     int n = strtol(buf, &end, 10);
} while (end != buf + strlen(buf));

Try using the following pattern in scanf. It will read until the end of the line:

scanf("%d\n", &n)

You won't need the getchar() inside the loop since scanf will read the whole line. The floats won't match the scanf pattern and the prompt will ask for an integer again.


Using fgets() is better.

To solve only using scanf() for input, scan for an int and the following char.

int ReadUntilEOL(void) {
  char ch;
  int count;
  while ((count = scanf("%c", &ch)) == 1 && ch != '\n')
    ; // Consume char until \n or EOF or IO error
  return count;
}

#include<stdio.h>
int main(void) {
  int n;

  for (;;) {
    printf("Please enter an integer: ");
    char NextChar = '\n';
    int count = scanf("%d%c", &n, &NextChar);
    if (count >= 1 && NextChar == '\n') 
      break;
    if (ReadUntilEOL() == EOF) 
      return 1;  // No valid input ever found
  }
  printf("You entered: %d\n", n);
  return 0;
}

This approach does not re-prompt if user only enters white-space such as only Enter.