Почему scanf() вызывает бесконечный цикл в этом коде?
У меня небольшая C-программа, которая просто считывает числа из stdin, по одному в каждом цикле цикла. Если пользователь вводит несколько NaN, на консоль должна быть напечатана ошибка, и приглашение ввода должно вернуться снова. При вводе "0" цикл должен заканчиваться, и количество заданных положительных/отрицательных значений должно быть напечатано на консоли. Здесь программа:
#include <stdio.h>
int main()
{
int number, p = 0, n = 0;
while (1) {
printf("-> ");
if (scanf("%d", &number) == 0) {
printf("Err...\n");
continue;
}
if (number > 0) p++;
else if (number < 0) n++;
else break; /* 0 given */
}
printf("Read %d positive and %d negative numbers\n", p, n);
return 0;
}
Моя проблема заключается в том, что при вводе некоторого не числа (например, "a" ) это приводит к бесконечному циклу, пишущему "- > Err..." снова и снова. Я предполагаю, что это проблема scanf(), и я знаю, что эта функция может быть заменена более безопасной, но этот пример для новичков, зная только о printf/scanf, if-else и циклах.
Я уже прочитал ответы на этот вопрос и просмотрел другие вопросы, но ничего не ответил на эту конкретную проблему.
Ответы
Ответ 1
scanf
потребляет только вход, который соответствует строке формата, возвращая количество потребляемых символов. Любой символ, который не соответствует строкам формата, заставляет его остановить сканирование и оставить недопустимый символ в буфере. Как говорили другие, вам все равно нужно вымыть недопустимый символ из буфера, прежде чем продолжить. Это довольно грязное исправление, но оно удалит нарушающие символы с выхода.
char c = '0';
if (scanf("%d", &number) == 0) {
printf("Err. . .\n");
do {
c = getchar();
}
while (!isdigit(c));
ungetc(c, stdin);
//consume non-numeric chars from buffer
}
edit: исправлен код для удаления всех нечисловых символов за один раз. Не будет распечатывать несколько "Errs" для каждого нечислового char больше.
Здесь - довольно хороший обзор scanf.
Ответ 2
Я думаю, вам просто нужно сбросить буфер, прежде чем продолжить цикл. Что-то вроде этого, вероятно, выполнит эту работу, хотя я не могу проверить, что я пишу здесь:
int c;
while((c = getchar()) != '\n' && c != EOF);
Ответ 3
scanf()
оставит "a
" все еще в буфере ввода в следующий раз. Вероятно, вы должны использовать getline()
для чтения строки независимо от того, а затем проанализировать ее с помощью strtol()
или аналогичного.
(Да, getline()
является специфичным для GNU, а не POSIX. Итак, что? Вопрос помечен как "gcc" и "linux". getline()
также является единственной разумной опцией для чтения строки текста, если вы не хотите сделать все это вручную.)
Ответ 4
Из-за проблем с scanf
, указанных другими ответами, вы должны действительно рассмотреть возможность использования другого подхода. Я всегда считал scanf
слишком ограниченным для любого серьезного ввода и обработки ввода. Лучше всего просто прочитать целые строки с помощью fgets
, а затем работать с ними с такими функциями, как strtok
и strtol
(которые BTW будут правильно анализировать целые числа и сообщать вам, где именно начинаются недопустимые символы).
Ответ 5
Вместо использования scanf()
и нужно иметь дело с буфером с недопустимым символом, используйте fgets()
и sscanf()
.
/* ... */
printf("0 to quit -> ");
fflush(stdout);
while (fgets(buf, sizeof buf, stdin)) {
if (sscanf(buf, "%d", &number) != 1) {
fprintf(stderr, "Err...\n");
} else {
work(number);
}
printf("0 to quit -> ");
fflush(stdout);
}
/* ... */
Ответ 6
У меня была аналогичная проблема. Я решил только с помощью scanf.
Input "abc123<Enter>"
, чтобы увидеть, как это работает.
#include <stdio.h>
int n, num_ok;
char c;
main() {
while (1) {
printf("Input Number: ");
num_ok = scanf("%d", &n);
if (num_ok != 1) {
scanf("%c", &c);
printf("That wasn't a number: %c\n", c);
} else {
printf("The number is: %d\n", n);
}
}
}
Ответ 7
На некоторых платформах (особенно Windows и Linux) вы можете использовать fflush(stdin);
:
#include <stdio.h>
int main(void)
{
int number, p = 0, n = 0;
while (1) {
printf("-> ");
if (scanf("%d", &number) == 0) {
fflush(stdin);
printf("Err...\n");
continue;
}
fflush(stdin);
if (number > 0) p++;
else if (number < 0) n++;
else break; /* 0 given */
}
printf("Read %d positive and %d negative numbers\n", p, n);
return 0;
}
Ответ 8
Решение: Вам нужно добавить fflush(stdin);
, когда 0
возвращается из scanf
.
Причина: Кажется, что он оставляет входной символ в буфере при возникновении ошибки, поэтому каждый раз, когда вызывается scanf
, он просто пытается обработать недопустимый символ, но никогда не удаляет его из буфера. Когда вы вызываете fflush
, входной буфер (stdin) будет очищен, поэтому недопустимый символ больше не будет обрабатываться повторно.
Ваша программа изменена: Ниже приведена модификация вашей программы с необходимыми изменениями.
#include <stdio.h>
int main()
{
int number, p = 0, n = 0;
while (1) {
printf("-> ");
if (scanf("%d", &number) == 0) {
fflush(stdin);
printf("Err...\n");
continue;
}
if (number > 0) p++;
else if (number < 0) n++;
else break; /* 0 given */
}
printf("Read %d positive and %d negative numbers\n", p, n);
return 0;
}
Ответ 9
У меня была такая же проблема , и я нашел несколько хакерское решение. Я использую fgets()
для чтения ввода, а затем передаю его на sscanf()
. Это не плохое решение для задачи с бесконечным циклом, и с простым циклом я говорю C для поиска любого ни одного числового символа. Приведенный ниже код не позволит вводить такие данные, как 123abc
.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main(int argc, const char * argv[]) {
char line[10];
int loop, arrayLength, number, nan;
arrayLength = sizeof(line) / sizeof(char);
do {
nan = 0;
printf("Please enter a number:\n");
fgets(line, arrayLength, stdin);
for(loop = 0; loop < arrayLength; loop++) { // search for any none numeric charcter inisde the line array
if(line[loop] == '\n') { // stop the search if there is a carrage return
break;
}
if((line[0] == '-' || line[0] == '+') && loop == 0) { // Exculude the sign charcters infront of numbers so the program can accept both negative and positive numbers
continue;
}
if(!isdigit(line[loop])) { // if there is a none numeric character then add one to nan and break the loop
nan++;
break;
}
}
} while(nan || strlen(line) == 1); // check if there is any NaN or the user has just hit enter
sscanf(line, "%d", &number);
printf("You enterd number %d\n", number);
return 0;
}
Ответ 10
попробуйте использовать это:
if (scanf("%d", &number) == 0) {
printf("Err...\n");
break;
}
это сработало отлично для меня... попробуйте это..
оператор продолжить не подходит, поскольку Err.. должен выполняться только один раз. поэтому попробуйте перерыв, который я тестировал... это сработало отлично для вас.. я тестировал....
Ответ 11
Когда вводится не номер, возникает ошибка, а номер не сохраняется во входном буфере. Вы должны пропустить это. Также даже эта комбинация символов, например, 1a
, будет считана сначала как номер 1, я думаю, вы также должны пропустить такой ввод.
Программа может выглядеть следующим образом.
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int p = 0, n = 0;
while (1)
{
char c;
int number;
int success;
printf("-> ");
success = scanf("%d%c", &number, &c);
if ( success != EOF )
{
success = success == 2 && isspace( ( unsigned char )c );
}
if ( ( success == EOF ) || ( success && number == 0 ) ) break;
if ( !success )
{
scanf("%*[^ \t\n]");
clearerr(stdin);
}
else if ( number > 0 )
{
++p;
}
else if ( number < n )
{
++n;
}
}
printf( "\nRead %d positive and %d negative numbers\n", p, n );
return 0;
}
Выход программы может выглядеть как
-> 1
-> -1
-> 2
-> -2
-> 0a
-> -0a
-> a0
-> -a0
-> 3
-> -3
-> 0
Read 3 positive and 3 negative numbers
Ответ 12
Чтобы частично решить вашу проблему, я просто добавляю следующую строку после scanf:
fgetc(stdin); /* to delete '\n' character */
Ниже ваш код со строкой:
#include <stdio.h>
int main()
{
int number, p = 0, n = 0;
while (1) {
printf("-> ");
if (scanf("%d", &number) == 0) {
fgetc(stdin); /* to delete '\n' character */
printf("Err...\n");
continue;
}
if (number > 0) p++;
else if (number < 0) n++;
else break; /* 0 given */
}
printf("Read %d positive and %d negative numbers\n", p, n);
return 0;
}
Но если вы введете более одного символа, программа продолжит один за другим до "\n".
Поэтому я нашел решение здесь: Как ограничить длину ввода с помощью scanf
Вы можете использовать эту строку:
int c;
while ((c = fgetc(stdin)) != '\n' && c != EOF);
Ответ 13
Сбросьте входной буфер перед сканированием:
while(getchar() != EOF) continue;
if (scanf("%d", &number) == 0) {
...
Я собирался предложить fflush(stdin)
, но, по-видимому, это приводит к undefined поведение.
В ответ на ваш комментарий, если вы хотите, чтобы появилось приглашение, вы должны очистить выходной буфер. По умолчанию это происходит только при печати новой строки. Как:
while (1) {
printf("-> ");
fflush(stdout);
while(getchar() != EOF) continue;
if (scanf("%d", &number) == 0) {
...
Ответ 14
Привет, я знаю, что это старый поток, но я только что закончил школьное задание, где столкнулся с этой проблемой.
Мое решение состоит в том, что я использовал gets(), чтобы забрать то, что scanf() осталось позади.
Здесь код OP немного переписан; вероятно, ему не нужно, но, возможно, это поможет кому-то другому.
#include <stdio.h>
int main()
{
int number, p = 0, n = 0;
char unwantedCharacters[40]; //created array to catch unwanted input
unwantedCharacters[0] = 0; //initialzed first byte of array to zero
while (1)
{
printf("-> ");
scanf("%d", &number);
gets(unwantedCharacters); //collect what scanf() wouldn't from the input stream
if (unwantedCharacters[0] == 0) //if unwantedCharacters array is empty (the user input is valid)
{
if (number > 0) p++;
else if (number < 0) n++;
else break; /* 0 given */
}
else
printf("Err...\n");
}
printf("Read %d positive and %d negative numbers\n", p, n);
return 0;
}
Ответ 15
Добрый вечер. У меня недавно была одна и та же проблема, и я нашел решение, которое может помочь многим парням. Ну, на самом деле функция "scanf" оставляет буфер в памяти... и поэтому вызван бесконечный цикл. Таким образом, вам действительно нужно "сохранить" этот буфер для другой переменной, если ваш исходный scanf содержит значение "null". Вот что я имею в виду:
#include <stdio.h>
int n;
char c[5];
main() {
while (1) {
printf("Input Number: ");
if (scanf("%d", &n)==0) { //if you type char scanf gets null value
scanf("%s", &c); //the abovementioned char stored in 'c'
printf("That wasn't a number: %s\n", c);
}
else printf("The number is: %d\n", n);
}
}