Зачем нам нужен getchar () при использовании scanf ()?

этот код должен читать положительное число, и если пользователь вводит нечисловое значение, он снова просит его ввести число и ждать ввода для повторной проверки до ввода числа

 do
   {
        printf("Enter a positive number: ");
   }
  while ( (scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0) ) ;

но на самом деле при вводе нечислового числа он продолжает выполнять тело цикла while, не читая ожидание, чтобы проверить состояние цикла while while(scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0)

при редактировании условия на

int clean_stdin()
{
    while (getchar()!='\n');
    return 1;
}

 do
   {
        printf("Enter a positive number: ");
   }
  while ( (scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0) && clean_stdin() ) ;

Он выполняется правильно, но я не понимаю, зачем нам добавлять getchar(), хотя мы уже используем scanf() в условии


person بسمة صلاح    schedule 03.07.2017    source источник
comment
getch не является стандартным, Ваш код все равно его не использует.   -  person too honest for this site    schedule 03.07.2017
comment
Хотя формат %d очищает все начальные пробелы во входном буфере, clean_stdin необходим для учета регистра, когда вы вводили, скажем, 123z.   -  person Weather Vane    schedule 03.07.2017
comment
Я использую fseek, потому что вы можете прервать ввод с помощью CTRL-D. Таким образом, вы не можете вызвать getchar () для ожидания \ n.   -  person mattn    schedule 03.07.2017


Ответы (4)


Когда scanf("%d%c", ... встречает нечисловой ввод, "%d" останавливает сканирование и символ нарушения остается в stdin для следующей функции ввода. "%c" не имеет возможности прочитать этот нечисловой символ.

Если код перечитает stdin с тем же scanf("%d%c", ..., результат будет тот же. Требуется какой-то другой способ удалить нечисловой ввод. getchar(), getch() и т. Д. Будут читать любой 1 символ.

Пример кода GetPositiveNumber()

person chux - Reinstate Monica    schedule 03.07.2017

Рассмотрите возможность использования fgets для сбора входных данных. Любой недопустимый ввод уже удален из входного потока, что упрощает повторную попытку.
При необходимости проанализируйте ввод с помощью sscanf или других средств.

char input[40] = "";
int valid = 0;
int n = 0;

do {
    printf ( "Enter a positive integer\n");
    if ( fgets ( input, sizeof ( input), stdin)) {
        int last = 0;
        if ( 1 == ( valid = sscanf ( input, "%d%n", &n, &last))) {
            if ( n < 0  || input[last] != '\n') {
                valid = 0;//reset if negative or last is not newline
            }
        }
    }
    else {
        fprintf ( stderr, "problem getting input from fgets\n");
        return 0;
    }
} while ( valid != 1);
person xing    schedule 03.07.2017
comment
мой плохой, нет проблем с EOF. - person chqrlie; 03.07.2017

Поскольку scanf() с большинством спецификаторов игнорирует пробелы, и вам нужно собрать их перед повторным вызовом scanf(), если в буфере осталось '\n'. Если вы их не соберете (и немедленно выбросите), scanf() немедленно вернется при следующем вызове.

person Iharob Al Asimi    schedule 03.07.2017
comment
Не правда. Спецификатор формата %d удаляет все начальные пробелы, включая символы новой строки. - person Weather Vane; 03.07.2017
comment
@WeatherVane Пожалуйста, попробуйте просто while (((count = scanf("%d", &value)) != 1) && (count != EOF)), что тогда? - person Iharob Al Asimi; 03.07.2017

См. Этот код

#include <stdio.h>

int
main(int argc, char* argv[]) {
  char c = 0;
  scanf("%c", &c);
  printf("%c\n", c);
  scanf("%c", &c);
  printf("%c\n", c);
  return 0;
}

Если вы наберете более двух или трех символов, второй вызов scanf не будет работать как прерывистый. И getch () не является стандартным. Также не следует вызывать getchar (), так как он может блокировать. Вы можете использовать fseek.

Взлом с помощью fseek работает только в Windows. :-(

ПЛОХОЕ РЕШЕНИЕ

#include <stdio.h>

int
main(int argc, char* argv[]) {
  char c = 0;
  scanf("%c", &c);
  printf("%c\n", c);
  fseek(stdin, 0, SEEK_END);
  scanf("%c", &c);
  printf("%c\n", c);
  return 0;
}

ХОРОШЕЕ РЕШЕНИЕ

#include <stdio.h>

int
main(int argc, char* argv[]) {
  char buf[BUFSIZ];
  char c = 0;
  scanf("%c", &c);
  printf("%c\n", c);
  while (!feof(stdin) && getchar() != '\n');
  scanf("%c", &c);
  printf("%c\n", c);
  return 0;
}

Это хорошо работает в Windows и Linux.

person mattn    schedule 03.07.2017
comment
Использование fseek() на терминале в Unix-подобной системе ничего не дает; терминалы не являются устройствами, доступными для поиска. - person Jonathan Leffler; 03.07.2017
comment
Хм, ты прав. У меня работает только на винде. Как насчет использования setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, BUFSIZ);? - person mattn; 03.07.2017
comment
За мои деньги используйте getchar() до новой строки (или EOF). Если это не нормально, то практически любое решение зависит от платформы. Специфическая для платформы проблема не является проблемой, если платформа (и), на которой она работает, четко определены. Не представляйте решение для конкретной платформы как универсальное. Иногда, однако, вы заранее не знаете, что решение зависит от платформы. Например, Microsoft поддерживает fflush(stdin); Unix-подобные системы этого не делают. Microsoft может поддерживать fseek() на входных потоках терминала; Unix-подобные системы этого не делают. Если вы этого не знаете, это сложно. - person Jonathan Leffler; 03.07.2017
comment
обновленный пост. и мне кажется, что сначала мне лучше проверить feof. - person mattn; 03.07.2017
comment
Вместо проверки feof() после использования c рекомендуется проверять возвращаемое значение scanf() - оно равно 1? - person chux - Reinstate Monica; 03.07.2017
comment
даже если scanf вернет 1, буфер будет заполнен остальными символами. - person mattn; 03.07.2017
comment
Хм, Window поддерживает перемотку (stdin), но Windows не поддерживает select (fileno (stdin)). Возможно, платформенно-совместимого решения не существует. - person mattn; 03.07.2017
comment
scanf("%c", &c); может вернуть EOF. Тогда printf("%c\n", c); - неопределенное поведение. Лучше сначала проверить возвращаемое значение scanf(), прежде чем использовать c. - person chux - Reinstate Monica; 03.07.2017
comment
Обратите внимание, что while (!feof(file)) всегда неверно, но вы используете немного другую конструкцию что "ОК". Я бы по-прежнему предпочел: int c; while ((c = getchar()) != EOF && c != '\n') ; хотя бы потому, что это один вызов функции, а не два. - person Jonathan Leffler; 03.07.2017