Как сканировать только целое число?

Я хочу, чтобы код выполнялся до тех пор, пока пользователь не введет целочисленное значение.

Код работает для массивов char и char.

Я сделал следующее:


#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;
}

Проблема в том, что пользователь вводит значение float scanf, которое примет его.

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

Как этого можно избежать?

Ответ 1

  • Вы берете scanf().
  • Вы бросаете его в корзину.
  • Вы используете fgets() для получения всей строки.
  • Вы используете strtol() для анализа строки как целого числа, проверяя, потребляла ли она всю строку.
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));

Ответ 2

Используйте fgets и strtol,

Указатель на первый символ, следующий за целочисленным представлением в s, сохраняется в объекте, указанном p, если *p отличается от \n, то у вас неверный ввод.

#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;
}

Ответ 3

Попробуйте использовать следующий шаблон в scanf. Он будет читать до конца строки:

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

Вам не понадобится getchar() внутри цикла, поскольку scanf прочитает всю строку. Число с плавающей точкой не будет соответствовать шаблону scanf, и приглашение снова запросит целое число.

Ответ 4

Я знаю, как это можно сделать с помощью fgets и strtol, я хотел бы знать, как это можно сделать, используя scanf() (если возможно).

Как говорят другие, scanf для этого не подходит, fgets и strtol является альтернативой (хотя fgets имеет недостаток, что трудно обнаружить 0-байтовый вход и невозможно сказать, что было введено после 0-байтного, если оно есть).

Для полноты (и если допустимый ввод - целое число, за которым следует новая строка):

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

В качестве альтернативы используйте %n до и после %*1[\n] с подавлением присваивания. Обратите внимание, однако (из Страница руководства Debian):

Это не преобразование, хотя оно может быть подавлено с символом подавления присваивания *. В стандарте C говорится: "Выполнение директивы %n не увеличивает счетчик присваивания, возвращенный при завершении выполнения", но Исправление, похоже, противоречит этому. Вероятно, разумно не делать никаких предположений о влиянии преобразований %n на возвращаемое значение.

Ответ 5

Если вы используете scanf, вы можете сделать что-то вроде следующего:

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
}

Ответ 6

Использование fgets() лучше.

Чтобы разрешить только использование scanf() для ввода, сканируйте int и следующие 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;
}

Этот подход не запрашивает повторно, если пользователь вводит только пробел, например, только Enter.

Ответ 7

Возможное решение - подумать об этом назад: принять float как входной сигнал и отклонить вход, если float не является целым числом:

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

Хотя это позволяет пользователю вводить что-то вроде 12.0 или 12e0 и т.д.