int n = 0;
while (true) {
cin >> n;
if (cin) { // мы ввели целое число; теперь проверим его
if (1<=n && n<=10) break;
cout << "Извините, "
<< n << " выходит за пределы интервала [1:10];
попробуйте еще \n";
}
else if (cin.fail()) { // обнаружено нечто, что является
// целым числом
cin.clear(); // возвращаем поток в состояние good();
// мы хотим взглянуть на символы
cout << "Извините, это не число; попробуйте еще раз \n";
char ch;
while (cin>>ch && !isdigit(ch)); // отбрасываем не цифры
if (!cin) error(" ввода нет "); // цифры не обнаружены:
// прекратить
cin.unget(); // возвращаем цифру назад,
// чтобы можно было считать число
}
else {
error(" ввода нет "); // состояние eof или bad: прекратить
}
}
// если мы добрались до этой точки, значит, число n лежит
// в диапазоне [1:10]
Этот код запутан и многословен. На самом деле мы бы не рекомендовали людям писать такие программы каждый раз, когда они ждут от пользователя ввода целого числа. С другой стороны, мы должны предусматривать потенциальные ошибки, поскольку людям свойственно ошибаться. Так что же делать? Причина того, что этот код так запутан, заключается в том, что в нем перемешано сразу несколько проблем.
• Считывание значения.
• Предложение к вводу.
• Вывод сообщений об ошибках.
• Пропуск “плохих” входных символов.
• Проверка диапазона входных чисел.
Для того чтобы сделать код яснее, часто достаточно просто логически разделить задачи среди нескольких функций. Например, мы можем выделить код, восстанавливающий ввод после обнаружения “плохого” (т.е. неожиданного) символа.
void skip_to_int()
{
if (cin.fail()) { // обнаружено нечто, что является целым числом
cin.clear(); // возвращаем поток в состояние good();
// мы хотим взглянуть на символы
char ch;
while (cin>>ch){ // отбрасываем не цифры
if (isdigit(ch) || ch == '-')
cin.unget(); // возвращаем цифру назад,
// чтобы можно было считать число
}
}
}
error(" ввода нет "); // состояние eof или bad: прекратить
}
Имея вспомогательную функцию
skip_to_int()
, можем написать следующий код:
cout << "Пожалуйста, введите целое число от 1 до 10:\n";
int n = 0;
while (true) {
if (cin>>n) { // мы ввели целое число; теперь проверим его
if (1<=n && n<=10) break;
cout << "Извините, " << n
<< " выходит за пределы интервала [1:10]; попробуйте еще раз.\n";
}
else {
cout << "Извините, это не число; попробуйте еще раз.\n";
skip_to_int();
}
}
// если мы добрались до этой точки, значит, число n лежит
// в диапазоне [1:10]
Этот код лучше, но остается слишком длинным и запутанным для того, чтобы много раз применять его в программе. Мы никогда не добьемся желаемого результата, разве что после (слишком) долгой проверки. Какие операции мы бы хотели иметь на самом деле? Один из разумных ответов звучит так: “Нам нужны две функции: одна должна считывать любое число типа int, а другая — целое число из заданного диапазона”.
int get_int(); // считывает число типа int из потока cin
int get_int(int low, int high); // считывает из потока cin число int,
// находящееся в диапазоне [low:high]
Если бы у нас были эти функции, то мы могли бы, по крайней мере, использовать их просто и правильно. Их несложно написать.
int get_int()
{
int n = 0;
while (true) {
if (cin >> n) return n;
cout << "Извините, это не число; попробуйте еще раз \n";
skip_to_int();
}
}
В принципе функция
get_int()
упорно считывает данные, пока не найдет цифры, которые можно интерпретировать как целое число. Если требуется прекратить работу функции
get_int()
, то следует ввести целое число или признак конца файла (во втором случае функция
get_int()
сгенерирует исключение).