Цикл while к бесконечности, когда вход cin является точкой - PullRequest
0 голосов
/ 13 декабря 2011

У меня проблемы с использованием метода cin для получения переменной.Когда вводом является число, проблем не возникает, но когда это специальный символ, такой как точка [.], Whilelolo зацикливается на бесконечность.Что я делаю не так?

cout << "What is your race" <<endl<<"1.Human\n2.troll\n3.zombie"<<endl;
    cin >> *race;
    while(*race<1||*race>3)
    {
    system("cls");
    cout << "Wrong choice"<<endl<< "What is your race" <<endl<<"1.Human\n2.troll\n3.zombie"<<endl;
    cin >> *race;
    }

Я искал ответ, и мне нужно очистить буфер, но я не понимаю, как это сделать. Я довольно новичок в c ++. Спасибо

Ответы [ 4 ]

2 голосов
/ 13 декабря 2011

Сделайте race символом, тогда вы сможете:

while (*race < '1' || *race > '3')

, что, вероятно, то, чего вы хотите достичь.

Объяснение:

Когда вы cin >> в int, он преобразует данную строку ASCII в целочисленное значение.. не имеет целочисленного значения, поэтому он не считывается в race и failbit устанавливается - далее >> s не работают, пока вы не очистите их.Однако, если вы cin >> в char и сравните его с другими char с (ну, на самом деле, с их кодами ASCII), вы сможете проверить это без проблем.

1 голос
/ 13 декабря 2011

Есть несколько проблем с вашим кодом.Во-первых, вы не проверяете, что ваш ввод был успешным;правильное условие для while должно быть:

while ( !cin || (*race < 1 || *race > 3) )

Как написано, если ввод не удался (что происходит при вводе '.', предполагая, что race имеет тип int*), тогда *race содержит его предыдущее значение, каким бы оно ни было.

Во-вторых, если вы получаете ошибку от cin, вы не очищаете ее.Когда поток находится в состоянии ошибки, он остается таким до тех пор, пока вы не очистите его явно.Если cin потерпел неудачу, вам нужно выполнить:

cin.clear();

где-нибудь в цикле.

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

cin.ignore( INT_MAX, '\n' );

Возможно, вы захотите сделать это, даже если cin не потерпел неудачу, либо вцикл (если введен из-за условия *race < 1 || *race > 3) или в случае успеха.В качестве альтернативы вы можете перейти к чтению строк и убедиться, что строка содержит только пробелы после интересующего вас символа.

Это последнее решение, которое я бы принял, поскольку оно обрабатывает почти всеиз вышеперечисленных проблем.Поэтому мой код будет выглядеть примерно так:

//  return -1 on error in input,
//  throw exception on (unexpected) end of file
int
getRace( std::istream& source )
{
    std::string line;
    if ( !std::getline( source, line ) ) {
        throw std::ios_base::failure( "Unexpected end of file" );
    }
    std::istringstream tmp( line );
    int results;
    return tmp >> results >> std::ws && tmp.get() == EOF
        ? results
        : -1;
}

//  ...
int race = -1;
while ( race < 0 ) {
    std::cout << "What is your race\n"
                 "1. Human\n"
                 "2. Troll\n"
                 "3.  Zombie\n" << std::flush;
    race = getRace( std::cout );
    if ( race < 0 ) {
        std::cout << "Wrong choice" << std::endl;
    }
}

Обратите внимание, что при вводе через строку вы избегаете любых проблем с сбросом ошибок формата, пропуском ошибочного ввода или повторной синхронизацией в случае ошибки.

1 голос
/ 13 декабря 2011

Этот пример точно воспроизводит вашу проблему:

#include <iostream>

int main()
{
    int i = 5;
    while (i < 1 || i > 3)
    {
        std::cin >> i;
    }
}

Вот что происходит: когда operator>> не может прочитать целое число (например, когда вы набираете точку и нажимаете ввод), все, что вы ввели , остается в потоке , включая символ новой строки. Таким образом, в следующей итерации цикла while следующий вход уже существует, и, поскольку он не является допустимым целым числом, цикл никогда не может прерваться. Необходимо убедиться, что при сбое operator>> вы очищаете поток и очищаете все установленные флаги ошибок.

#include <iostream>
#include <limits>

int main()
{
    int i = 5;
    while (i < 1 || i > 3)
    {
        if (!(std::cin >> i))
        {
            // clear streams internal error flags
            std::cin.clear();
            // ignore what's left in the stream, up to first newline character
            // or the entire content, whichever comes first
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
    }
}
1 голос
/ 13 декабря 2011

Другое решение, кроме принятого, состоит в том, чтобы очистить сини failbit и игнорировать последний ввод, как показано ниже:

cout << "What is your race" <<endl<<"1.Human\n2.troll\n3.zombie"<<endl;
cin >> *race;
while(*race<1||*race>3)
{
    // Clears the state of cin to be in good state
    cin.clear();
    // Ignores the last input read so that it's not read back again
    cin.ignore();
    system("cls");
    cout << "Wrong choice"<<endl<< "What is your race" <<endl<<"1.Human\n2.troll\n3.zombie"<<endl;
    cin >> *race;
}
...