double temperature;
while (ist >> hour >> temperature) {
if (hour < 0 || 23
temps.push_back(Reading(hour,temperature));
}
for (int i=0; i
ost << '(' << temps[i].hour << ','
<< temps[i].temperature << ")\n";
}
10.6. Обработка ошибок ввода-вывода
Вводя данные, мы должны предвидеть ошибки и обрабатывать их. Какими бывают ошибки? Как их обрабатывать? Ошибки возникают из-за того, что их совершают люди (неправильно поняли инструкцию, сделали опечатку, по клавиатуре прошлась кошка и т.д.), из-за того, что файлы не соответствуют спецификациям, из-за того, что программисты имеют неправильное представление об ожидаемых данных, и т.д. Возможности для совершения ошибок при вводе данных ничем не ограничены! Однако поток istream
сводит их все к четырем возможным классам, которые называют
fail()
и bad()
определены неточно и зависят от точки зрения программистов на определение операций ввода-вывода для новых типов. Однако основная идея проста: если операция ввода обнаруживает простую ошибку форматирования, она позволяет потоку вызвать функцию fail()
, предполагая, что вы (пользователь операции ввода) способны ее исправить. Если же, с другой стороны, произошло нечто совершенно ужасное, например неправильное чтение с диска, то операция ввода позволяет потоку вызвать функцию bad()
, предполагая, что вам ничего не остается делать, кроме как отказаться от попытки считать данные из потока. Это приводит нас к следующей общей логике:
int i = 0;
cin >> i;
if (!cin) { // мы окажемся здесь (и только здесь),
// если операция ввода не выполнена
if (cin.bad()) error("cin испорчен "); // поток поврежден: стоп!
if (cin.eof()) {
// входных данных больше нет
// именно так мы хотели бы завершить ввод данных
}
if (cin.fail()) { // с потоком что-то случилось
cin.clear(); // приготовиться к дальнейшему вводу
// исправление ситуации
}
}
Выражение !cin
можно прочитать как “поток cin
в плохом состоянии”, или “с потоком cin
что-то случилось”, или “поток cin
не находится в состоянии good()
”. Это выражение противоположно по смыслу выражению “операция успешно завершена”. Обратите внимание на инструкцию cin.clear()
, в которой обрабатывается состояние fail()
. Если поток поврежден, то мы, вероятно, можем его восстановить. Для того чтобы сделать это, мы явно выводим поток из состояния fail()
и можем снова просматривать последовательность символов, находящихся в этом потоке; функция clear()
гарантирует, что после выполнения вызова cin.clear()
поток cin
перейдет в состояние good()
.
Рассмотрим пример использования состояния потока. Представим себе, что считываем в вектор последовательность целых чисел, которые могут завершаться символом *
или признаком конца файла (
1 2 3 4 5 *
Ввести их можно с помощью такой функции:
void fill_vector(istream& ist, vector
// считывает целые числа из потока ist в вектор v,
// пока не будет достигнут признак eof() или символ завершения
{
int i = 0;
while (ist >> i) v.push_back(i);
if (ist.eof()) return; // отлично: мы достигли конца файла
if (ist.bad()) error("Поток ist поврежден."); // поток поврежден;
// стоп!
if (ist.fail()) { // очищаем путаницу как можем и сообщаем
// об ошибке
ist.clear(); // очищаем состояние потока
// и теперь снова можем искать признак
// завершения
char c;
ist>>c; // считываем символ, возможно, признак
// завершения
if (c != terminator) { // неожиданный символ