Читать интересную книгу Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 106 107 108 109 110 111 112 113 114 ... 337
(ist.eof()) return; // отлично: мы достигли конца файла

  if (ist.bad()) error("Поток ist поврежден."); // поток поврежден;

                                               // стоп!

  if (ist.fail()) { // очищаем путаницу как можем и сообщаем

                    // об ошибке

    ist.clear();    // очищаем состояние потока

                    // и теперь снова можем искать признак

                    // завершения

    char c;

    ist>>c;         // считываем символ, возможно, признак

                    // завершения

    if (c != terminator) {          // неожиданный символ

      ist.unget();                  // возвращаем этот символ назад

      ist.clear(ios_base::failbit); // переводим поток

                                    // в состояние fail()

    }

  }

}

Обратите внимание на то, что пока мы не найдем признак конца файла, мы не выйдем из цикла. Кроме того, мы можем собрать некоторые данные, и функция, вызвавшая функцию fill_vector(), может попытаться вывести поток из состояния fail(). Поскольку мы очистили состояние, то, для того чтобы проверить символ, должны вернуть поток обратно в состояние fail(). Для этого выполняется инструкция ist.clear(ios_base::failbit). Обратите внимание на потенциально опасное использование функции clear(): на самом деле функция clear() с аргументом устанавливает указанные флаги (биты) состояния потока iostream, сбрасывая (только) не указанные. Переводя поток в состояние fail(), мы указываем, что обнаружили ошибку форматирования, а не нечто более серьезное. Мы возвращаем символ обратно в поток ist, используя функцию unget(); функция, вызывающая функцию fill_vector(), может использовать его по своему усмотрению. Функция unget() представляет собой более короткий вариант функции putback(), который основывается на предположении, что поток помнит, какой символ был последним, и поэтому его не обязательно указывать явно.

Если вы вызвали функцию fill_vector() и хотите знать, что вызвало прекращение ввода, то можно проверить состояния fail() и eof(). Кроме того, можно перехватить исключение runtime_error, сгенерированное функцией error(), но понятно, что маловероятно получить больше данных из потока istream, находящегося в состоянии bad(). Большинство вызывающих функций не предусматривает сложной обработки ошибок. По этой причине практически во всех случаях единственное, чего мы хотим сделать, обнаружив состояние bad(), — сгенерировать исключение.

 

 Для того чтобы облегчить себе жизнь, можем поручить потоку istream сделать это за нас.

// поток ist генерирует исключение, если попадает в состояние bad

ist.exceptions(ist.exceptions()|ios_base::badbit);

Эти обозначения могут показаться странными, но результат простой: если поток ist окажется в состоянии bad(), он сгенерирует стандартное библиотечное исключение ios_base::failure. Вызвать функцию exceptions() можно только один раз. Все это позволяет упростить циклы ввода, игнорируя состояние bad().

void fill_vector(istream& ist, vector<int>& v, char terminator)

 // считываем целые числа из потока ist в вектор v, пока не

 // достигнем конца файла eof() или признака завершения

{

  int i = 0;

  while (ist >> i) v.push_back(i);

  if (ist.eof()) return; // отлично: обнаружен конец файла

               // не good(), не bad() и не eof(),

               // поток ist должен быть переведен в состояние fail()

  ist.clear(); // сбрасываем состояние потока

  char c;

  ist>>c; // считываем символ в поисках признака завершения ввода

  if (c != terminator) { // Ох: это не признак завершения ввода,

                         // значит, нужно вызывать функцию fail()

    ist.unget();         // может быть, вызывающая функция

                         // может использовать этот символ

    ist.clear(ios_base::failbit); // установить состояние fail()

  }

}

Класс ios_base является частью потока iostream, в котором хранятся константы, такие как badbit, исключения, такие как failure, и другие полезные вещи. Для обращения к нему необходим оператор ::, например ios_base::badbit (раздел B.7.2). Мы не планируем подробно описывать библиотеку iostream; для этого понадобился бы отдельный курс лекций. Например, потоки iostream могут обрабатывать разные наборы символов, реализовывать разные стратегии буферизации, а также содержат средства форматирования представлений денежных средств на разных языках (однажды мы даже получили сообщение об ошибке, связанной с форматированием представления украинской валюты). Все, что вам необходимо знать о потоках iostream, можно найти в книгах Страуструп (Stroustrup), The C++ Programming Language Страуструпа и Лангер (Langer), Standard C++ IOStreams and Locales.

Поток ostream имеет точно такие же состояния, как и поток istream: good(), fail(), eof() и bad(). Однако в таких программах, которые мы описываем в этой книге, ошибки при выводе встречаются намного реже, чем при вводе, поэтому мы редко их проверяем. Если вероятность того, что устройство вывода недоступно, переполнено или сломано, является значительной, то в программе следует предусмотреть проверку состояния потока вывода после каждой операции вывода, так как мы сделали выше по отношению к операции ввода.

10.7. Считывание отдельного значения

Итак, мы знаем, как считать последовательность значений, завершающихся признаком конца файла или завершения ввода. Впоследствии мы рассмотрим еще несколько примеров, а сейчас обсудим все еще популярную идею о том, чтобы несколько раз запрашивать значение, пока не будет введен его приемлемый вариант. Это позволит нам проверить несколько распространенных проектных решений. Мы обсудим эти альтернативы на примерах нескольких решений простой проблемы — как получить от пользователя приемлемое значение. Начнем с очевидного, но скучного и запутанного варианта под названием “сначала попытайся”, а затем станем его постепенно совершенствовать. Наше основное предположение заключается в том, что мы имеем дело с интерактивным вводом, в ходе которого человек набирает на клавиатуре входные данные и читает сообщения, поступающие от программы. Давайте предложим пользователю ввести целое число от 1 до 10 (включительно).

cout << "Пожалуйста, введите целое число от 1 до 10:n";

int n = 0;

while (cin>>n) {            // читаем

  if (1<=n && n<=10) break; // проверяем диапазон

  cout << "Извините " << n

  << " выходит за пределы интервала [1:10]; попробуйте еще n";

}

Этот код довольно уродлив, но отчасти работоспособен. Если вы не любите использовать оператор break (раздел А.6), то можете объединить считывание и проверку диапазона.

cout << "Пожалуйста, введите целое число от 1 до 10:n";

int n = 0;

while (cin>>n && !(1<=n && n<=10)) // read and check

1 ... 106 107 108 109 110 111 112 113 114 ... 337
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп.
Книги, аналогичгные Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Оставить комментарий