Зачем отбрасывать символы? Попробуем решить такую задачу: нужно прочитать из cin целое число, причем если вместо целого числа нам подсунут какую-нибудь гадость, нужно сообщить об этом и попытаться прочитать число снова. Попытка номер один:
1
2
3
4
5
6
7
| using namespace std;
int value = 0;
while (not (cin >> value)) {
cout << "Invalid format" << endl;
cin.clear();
}
cout << "Your input: " << value << endl; |
Попытавшись ввести нечто, непохожее на целое число, получим бесконечный цикл. Попытка прочитать число в строке 3 заканчивается неудачей, но введенные символы из буфера ввода никуда не исчезают. Повторное чтение, натыкаясь на те же самые символы, заканчивается с тем же результатом.
Беглый гуглеж приводит меня к функции istream::sync(), которая, как говорит один источник, делает именно то, что нам нужно:
Synchronizes the buffer associated with the stream to its controlled input sequence. This effectively means that the unread characters in the buffer are discarded.
Что ж, попробуем:
1
2
3
4
5
6
7
8
| using namespace std;
int value = 0;
while (not (cin >> value)) {
cout << "Invalid format" << endl;
cin.clear();
cin.sync();
}
cout << "Your input: " << value << endl; |
Хм. В моей системе результат не изменился, код работает точно так же, как и первый. Беда в том, что функция istream::sync() в некоторых реализациях стандартной библиотеки таки отбрасывает непрочитанные символы. Но не в моем случае.
А я человек простой — лезу в код своей стандартной библиотеки и смотрю что да как. Выходит, что istream::sync() вызывает stdio_sync_filebuf::pubsync(), которая вызывает stdio_sync_filebuf::sync(), которая, наконец, вызывает fflush(stdin). Понятно, что fflush(stdin) не может оказать никакого влияния на входной буфер cin.
Так что упомянутый выше «один источник» не стоит считать истиной в последней инстанции. Но надо сказать, что попытки найти истину в стандарте — еще более неблагодарное занятие.
В общем, правильное решение заключается в использовании функции istream::ignore() таким вот образом:
1
2
3
4
5
6
7
8
| using namespace std;
int value = 0;
while (not (cin >> value)) {
cout << "Invalid format" << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
cout << "Your input: " << value << endl; |
Надо отметить, что вызов ignore() неплохо бы вставлять даже после успешного ввода, так как при вводе строки «123qwe» первые три символа будут прочитаны и распознаны как число, а «qwe» останутся во входном буфере и будут портить жизнь при следующем чтении. Еще одна тонкость — обработка конца файла. Если пользователь в ответ на приглашение к вводу нажмет Ctrl+D или что там в вашей системе для этого предназначено, то программа вместо того, чтобы завершиться, сообщит о неверном формате и предложит ввести число еще раз. Поэтому по-хорошему код должен быть вроде этого (вся программа):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| #include <iostream>
#include <limits>
using namespace std;
template <typename Type>
bool read_value(const std::string &prompt, Type &value)
{
bool repeat = true;
bool eof = cin.eof();
while (repeat and not eof) {
cout << prompt << ": ";
cin >> value;
eof = cin.eof();
repeat = cin.fail() and not eof;
if (repeat) {
cout << "***Invalid data format!" << endl;
cin.clear();
} else if (eof) {
cout << "(EOF)" << endl;
}
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
return not eof;
}
int main()
{
int value = 0;
while (read_value("Input integer", value))
cout << "Your input: " << value << endl;
} |
Но и в этом, казалось бы, безошибочном, коде затаилась пара граблей. Например, что если функцию read_value() использовать для ввода символов или строк? Сумеем мы ввести символ пробела? Сумеем ли ввести строку из нескольких слов, разделенных пробелами? Хрен! С вводом символа пробела еще несложно — надо только использовать манипулятор noskipws, а вот ввод строк с пробелами потребует перегрузки read_value() и использования getline() вместо оператора >>.
В общем, писать на C++ — сплошное счастье.




(1 голосов, средний: 5,00 из 5)
Loading ...