scanf игнорирование, бесконечный цикл - PullRequest
3 голосов
/ 13 марта 2011
int flag = 0;
int price = 0;
while (flag==0)
{
    printf("\nEnter Product price: ");
    scanf("%d",&price);
    if (price==0) 
        printf("input not valid\n"); 
    else 
        flag=1;
}

Когда я ввожу действительное число, цикл завершается, как и ожидалось.Но если я введу что-то, что не является числом, например hello, то код перейдет в бесконечный цикл.Он просто продолжает печатать Enter Product price: и input not valid.Но мне не нужно вводить новый номер.Почему это так?

Ответы [ 6 ]

4 голосов
/ 13 марта 2011

Когда вы вводите что-то, что не является числом, scanf завершится ошибкой и оставит эти символы на входе.Поэтому, если вы введете hello, scanf увидит h, отклонит его как недействительное для десятичного числа и оставит его на входе.В следующий раз в цикле scanf снова увидит h, так что цикл будет продолжаться вечно.

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

Что-то вроде этих строк:

char buffer[STRING_SIZE];
...
while(...) {
    ...
    fgets(buffer, STRING_SIZE, stdin);
    if ( sscanf(buffer, "%d", &price) == 1 )
        break;   // sscanf succeeded, end the loop
    ...
}

Если вы просто сделаете getchar, как предложено в другом ответе,тогда вы можете пропустить символ \n в случае, если пользователь вводит что-то после числа (например, пробел, возможно, после других символов).

Вы всегда должны проверять возвращаемое значение sscanf.Он возвращает количество назначенных преобразований, поэтому, если возвращаемое значение не совпадает с количеством запрошенных преобразований, это означает, что анализ не выполнен.В этом примере запрошено 1 преобразование, поэтому sscanf возвращает 1 в случае успеха.

3 голосов
/ 13 марта 2011

Формат% d предназначен для десятичных чисел. При сбое scanf (что-то другое вводится десятичное число) символ, вызвавший его сбой, останется в качестве ввода.

Пример.

    int va;
    scanf("%d",&va);
    printf("Val %d 1 \n", val);

    scanf("%d",&va);
    printf("Val %d 2 \n", val);
    return 0;

Так что никакого преобразования не происходит.

Функция scanf возвращает значение макроса EOF, если перед входом произошел сбой любое преобразование. В противном случае функция scanf возвращает количество элементов ввода назначенный, который может быть меньше, чем предусмотрено, или даже ноль, в случае раннего ошибка соответствия

7.19.6. Функция scanf - JTC1 / SC22 / WG14 - C

Так что вы должны заметить, что scanf возвращает свою собственную форму уведомления об успехе

int scanf(char *format)

чтобы вы могли также сделать следующее

do {
        printf("Enter Product \n");
}
while (scanf("%d", &sale.m_price) == 1);

if(scanf("%d", &sale.m_price) == 0)
        PrintWrongInput();

Также держите затылок, чтобы держаться подальше от scanf. scanf или scan в формате не должны использоваться для интерактивного ввода данных пользователем. См. C FAQ 12.20

1 голос
/ 13 марта 2011

"Ответы", которые говорят, что это произойдет, потому что в буфере есть '\ n', ошибочны - scanf("%d", ...) пропускает пробел, включая переводы строк.

Он переходит в бесконечный цикл, если x содержит 0, а scanf встречает не-число (не только пробел) или EOF, потому что x останется 0, и у него нет способа стать иначе.Это должно быть ясно, просто взглянув на ваш код и подумав, что он будет делать в этом случае.

1 голос
/ 13 марта 2011

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

Это можно исправить, прочитав '\ n' с помощью вызова getchar () после scanf.

0 голосов
/ 31 декабря 2013

Редактировать: Назад, когда я впервые написал этот ответ, я был настолько глуп и неосведомлен о том, как scanf() работал.

  • Прежде всего, позвольте мне кое-что прояснить, scanf() - не нарушенная функция, если я не знаю, как работает scanf() и не знаю, как ее использовать, то, вероятно, я не читал руководство для scans(), и это не может быть ошибкой scanf().
  • Во-вторых, чтобы понять, что не так с вашим кодом, вам нужно знать, как работает scanf().

Когда вы используете scanf("%d", &price) в своем коде, scanf() пытается прочитать integer из ввода, но если вы введете не числовое значение, scanf() знает, что это неправильный тип данных , таким образом, он помещает входные данные для чтения обратно в буфер в следующем цикле цикла, однако неверный ввод все еще находится в буфере, что приведет к повторному сбою scanf(), поскольку буфер не был очищен, и этот цикл продолжается вечно .

Для решения этой проблемы вы можете использовать возвращаемое значение scanf(), которое будет числом успешных прочитанных входов, однако вам нужно отбросить недопустимые входы, очистив буфер, чтобы избежать бесконечного цикла, буфер ввода очищается при нажатии клавиши enter, вы можете сделать это, используя функцию getchar(), чтобы сделать паузу для получения ввода, что потребует от вас нажатия клавиши enter, тем самым отбрасывая недопустимый ввод, обратите внимание, что это не заставит вас дважды нажать клавишу enter, независимо от того, введен или нет правильный тип данных, потому что newline character все еще будет в буфере. После того, как scanf() успешно завершит чтение integer из ввода, он поместит \n обратно в буфер, поэтому getchar() будет читать его, но, так как он вам не нужен, его безопасно отбросить:

#include <stdio.h>

int main(void)
{
    int flag = 0;
    int price = 0;
    int status = 0;
    while (flag == 0 && status != 1)
    {
        printf("\nEnter Product price: ");
        status = scanf("%d", &price);
        getchar();
        if (price == 0) 
            printf("input not valid\n"); 
        else 
            flag = 1;
    }   

    return 0;
}
0 голосов
/ 13 марта 2011

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

if (! Scanf ("% d", & sale.m_price)) fflush (STDIN);

...