Как предотвратить неуправляемый цикл ввода, когда я запрашиваю число, но пользователь вводит не число?

Мне нужно знать, как сделать, чтобы мой оператор cin не «удалял» себя, если вы введете неправильный тип. Код здесь:

int mathOperator()
{
  using namespace std;

  int Input;
  do
  {
    cout << "Choose: ";
    el();
    cout << "1) Addition";
    el();
    cout << "2) Subtraction";
    el();
    cout << "3) Multiplication";
    el();
    cout << "4) Division";
    el();
    el();
    cin >> Input;

  }
  while (Input != 1 && Input != 2 && Input!=3 && Input!=4);
  return Input;
}

Выполните, введите, например, символ, и он безостановочно зациклится, действуя так, как будто оператора cin здесь нет.


person Ashley Davies    schedule 01.02.2011    source источник
comment
Я думаю, что у всех когда-то была эта проблема. Считай это обрядом посвящения, Эшли.   -  person Rob Kennedy    schedule 01.02.2011


Ответы (6)


После чтения неверного значения cin находится в состоянии «сбой». Вы должны сбросить это.

Вы должны как сбросить флаг ошибки, так и очистить буфер. таким образом:

   cin.clear(); 
   cin.ignore(std::numeric_limits<streamsize>::max(), '\n');

Второй вызов «очищает» входной буфер от любых данных, которые могут быть там, чтобы вы были готовы к следующему вызову «cin».

Если вы обнаружите, что пишете эти 2 строки «по всему коду», вы можете написать простую встроенную функцию, чтобы заменить их.

   inline void reset( std::istream & is )
   {
       is.clear();
       is.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
   }

Хотя я заставил эту функцию принимать любой istream, в большинстве случаев она будет использоваться только для cin, когда пользователь вводит что-то недопустимое. Если это недопустимый ввод файла или строкового потока, исправить это невозможно, и вам лучше всего просто создать исключение.

person CashCow    schedule 01.02.2011
comment
поставить что-то вместо numeric_limits? Если да, то? Спасибо - person LazerSharks; 17.01.2013

Вы должны проверить, что ввод выполнен успешно, и обработать, если это не так:

int mathOperator() {
  using namespace std;

  int Input;
  do {
    cout << "Choose: ";
    el();
    cout << "1) Addition";
    el();
    cout << "2) Subtraction";
    el();
    cout << "3) Multiplication";
    el();
    cout << "4) Division";
    el();
    el();
    while (!(cin >> Input)) {  // failed to extract
      if (cin.eof()) {  // testing eof() *after* failure detected
        throw std::runtime_error("unexpected EOF on stdin");
      }
      cin.clear();  // clear stream state
      cin.ignore(INT_MAX, '\n');  // ignore rest of line
      cout << "Input error.  Try again!\n";
    }
  } while (Input != 1 && Input != 2 && Input!=3 && Input!=4);
  return Input;
}

Если вы не проверите успешность извлечения, cin останется в состоянии сбоя (cin.fail()). Оказавшись в состоянии сбоя, последующие извлечения немедленно вернутся вместо попыток чтения из потока, что фактически сделает их недействующими, что приведет к бесконечному циклу.

person Fred Nurk    schedule 01.02.2011
comment
+1 за решение основной проблемы. Еще как написал hilal, наверное это чар его интересует - person Daniel Wedlund; 01.02.2011
comment
@DanielPersson: я сомневаюсь. - person Fred Nurk; 01.02.2011

Если вы не достаточно уверены в правильности формата ввода, вам вряд ли захочется использовать operator>> непосредственно из потока ввода.

Обычно легче прочитать строку с std::getline, поместить ее в std::istringstream и читать оттуда. Если это не удается, вы печатаете/регистрируете сообщение об ошибке, отбрасываете оставшуюся часть строки и (возможно) переходите к следующей строке.

person Jerry Coffin    schedule 01.02.2011

не читайте int, читайте char, чтобы cin пропустил любой недопустимый символ

person Andriy Tylychko    schedule 01.02.2011
comment
это сработает, но не научит пользователя сбрасывать плохой cin - person CashCow; 01.02.2011
comment
Это лишь частичное решение по многим причинам, включая то, как обрабатывать 10 вариантов. - person Fred Nurk; 01.02.2011

char Input;

 do
 {
// same code 
 }
 while (Input != '1' && Input != '2' && Input != '3' && Input!='4');
 return Input;

[ИЗМЕНИТЬ]

Если вы хотите преобразовать char в int, вы можете использовать этот фрагмент кода

int i = (Input - 48);
person Community    schedule 01.02.2011
comment
Спасибо, я бы не подумал об этом :) есть ли какая-нибудь функция с функциональностью, такой как что-то вроде ChangeToNumber(char a), поэтому мне не придется слишком много переписывать, у меня есть несколько функций, которые полагаются на целочисленное значение . - person Ashley Davies; 01.02.2011
comment
Использовать 48 напрямую — паршивая идея. По крайней мере, Input-'0' (и то только после того, как вы убедитесь, что прочитанный вами символ является цифрой -- например, с isdigit). - person Jerry Coffin; 01.02.2011
comment
@Jerry - Ввод - «0» (что более элегантно) работает, даже если ввод не является цифрой, поскольку вы ограничены [0, 9]. Все, что находится за пределами этого диапазона, немедленно скажет вам, что это не цифра и, следовательно, неверный ввод. - person Zac Howland; 01.02.2011

Я согласен, что char так же удобен, поскольку вы всегда можете привести к int, чтобы ответить на ваш вопрос, почему это происходит, когда ввод cin выполняется как int, но вводится char, ввод сохраняется в входной поток на время цикла, поэтому он как бы «исчезает».

Для получения дополнительной информации см. сообщение Наруэ по адресу http://www.daniweb.com/forums/thread11505.html

person Michael    schedule 01.02.2011