Из языкового стандарта C (n1256) :
7.19.6.2 Функция fscanf
...
4 Функция fscanf выполняетсякаждая директива формата по очереди.В случае сбоя директивы, как описано ниже, функция возвращается.Сбои описываются как сбои ввода (из-за возникновения ошибки кодирования или недоступности вводимых символов) или сбоев сопоставления (из-за неправильного ввода).
...
7 Директива, которая является спецификацией преобразованияопределяет набор соответствующих входных последовательностей, как описано ниже для каждого спецификатора.Спецификация преобразования выполняется в следующие шаги:
8 Входные пробельные символы (как определено функцией isspace) пропускаются, если в спецификации не указано [, cСпецификатор или n . 250)
9 Элемент ввода считывается из потока, если в спецификации не указано n спецификатор.Элемент ввода определяется как самая длинная последовательность входных символов, которая не превышает заданную ширину поля и которая является или является префиксом соответствующей входной последовательности. 251) Первый символ, если имеется,после ввода элемент остается непрочитанным.Если длина входного элемента равна нулю, выполнение директивы заканчивается неудачей;это условие является ошибкой сопоставления, если только конец файла, ошибка кодирования или ошибка чтения не предотвратили ввод из потока, и в этом случае это сбой ввода.
10 За исключением случая со спецификатором % , элемент ввода (или, в случае директивы % n , количество вводимых символов)преобразован в тип, соответствующий спецификатору преобразования. Если элемент ввода не совпадает с последовательностью, выполнение директивы завершается неудачно: это условие является ошибкой совпадения. Если подавление присваивания не было указано *, результат преобразования помещается в указанный объектпервым аргументом после аргумента формата, который еще не получил результат преобразования.Если этот объект не имеет подходящего типа или если результат преобразования не может быть представлен в объекте, поведение не определено.
Выделение добавлено в параграф 10. Спецификатор преобразования %d
ожидает, что входной текст будет отформатирован как десятичное целое число.Если это не так, преобразование завершается неудачно, и символ, вызвавший сбой преобразования, остается во входном потоке.Дальнейшие вызовы scanf()
со спецификатором преобразования %d
будут подавлены тем же символом.
scanf()
возвращает количество успешных назначений;вам нужно проверить этот результат, чтобы убедиться, что преобразование прошло успешно, например:
int x = 0;
while (x != 4)
{
int result = scanf("%d", &x);
if (result != 1)
{
printf("Last call to scanf() failed; exiting\n");
break;
}
}
К сожалению, у вас все еще плохой ввод, застрявший во входном потоке.Существует ряд стратегий для борьбы с этим.Вы можете удалить ошибочный символ с помощью getchar
и повторить попытку:
while (x != 4)
{
int tmp;
if (scanf("%d", &tmp) == 0)
getchar();
else
x = tmp;
}
Или вы можете попытаться прочитать до следующего символа новой строки, предполагая, что весь оставшийся ввод b0rked:
while (x != 4)
{
int tmp;
if (scanf("%d", &tmp) == 0)
while (getchar() != '\n')
;
else
x = tmp;
}
Или вы можете попытаться прочитать ввод как текст и преобразовать в целое число, используя strtol()
(мой предпочтительный метод):
char input[SOME_SIZE];
int x = 0;
...
while (x != 4)
{
if (fgets(input, sizeof input, stdin))
{
char *check;
int tmp = (int) strtol(input, &check, 10);
if (!isspace(*check) && *check != 0)
{
printf("%s is not a valid integer: try again\n", input);
}
else
{
x = tmp;
}
}
else
{
printf("Read error on standard input\n");
break;
}
}
Это больше работы, но позволяет вампоймать неверный ввод до , ему присваивается x
.