|
|
От: |
LaptevVV
|
|
| Дата: | 25.12.06 07:03 | ||
| Оценка: | |||
I>>#include <iostream>
I>>using namespace std;
I>>int main()
I>>{
I>> int i;
I>> if (cin >> i)
I>> cout << "i = " << i << '\n';
I>> else
I>> cout << "error\n";
I>>}
I>>Состояния потока
Каждый поток (и стандартные в том числе) в каждый момент времени находится в некотором состоянии. Эти состояния имеют названия good, bad, fail и eof (end-of-file). Сами состояния определены в классе ios_base как целые статические константы. Эти константы называют флагами состояния потока [1-27.4.2.1.3].
typedef int iostate; goodbit = 0x00 badbit = 0x01 eofbit = 0x02 failbit = 0x04
Значения флагов зависят от реализации (показанные значения флагам присвоены в системе C++ Builder 6), однако имена определены в стандарте, поэтому именно так флаги состояния называются во всех без исключения системах. В программе имена флагов состояния нужно записывать с префиксом, например
std::ios_base::eofbit
С таким же успехом можно использовать класс, производный от ios_base. В частности, в текстах программ стандартной библиотеки часто встречается более короткий префикс
std::ios::eofbit
В базовом классе ios_base определено поле
iostate _M_iostate; // библиотека STLport
ПРИМЕЧАНИЕ
Такое имя поле имеет в системе Borland C++ Builder 6. В другой реализации имя поля может быть другим.
В этом поле сохраняются флаги состояния во время работы программы. Это поле мы можем прочитать методом
iostate rdstate();
Установить любой флаг можно методом
void setstate(iostate flag);
Для установки нескольких флагов, естественно, нужно воспользоваться битовыми операциями, например
setstate(std::ios::eofbit | std::ios::failbit);
Система ввода/вывода С++ предоставляет несколько методов, с помощью которых мы всегда можем узнать состояние потока:
bool good() const; // следующая операция может выполняться bool eof() const; // виден конец ввода bool fail() const; // следующая операция не выполняется bool bad() const; // поток испорчен
Если после выполнения некоторой операции поток находится в состоянии good, то это хорошая ситуация: во время предыдущей операции не произошло никаких непредвиденных событий и может быть выполнена следующая операции ввода/вывода. В остальных случаях следующая операция выполнена не будет.
Состояние fail и состояние bad являются состояниями ошибки потока. Если поток находится в одном из этих состояний, то операции обмена не выполняются. Состояние bad включает в себя состояние fail: когда поток находится в состоянии bad, он находится и в состоянии fail; обратное неверно.
Если поток находится в состоянии fail, то операция ввода/вывода завершилась неудачно, однако поток не испорчен и никакие символы не потеряны. Обычно это состояние устанавливается при ошибках форматирования в процессе чтения — например, программа пытается прочитать целое число, а первый же символ является недопустимым (буква или знак операции). Поток из состояния fail мы можем вернуть в нормальное состояние с помощью метода clear(), например
if (stream.fail()) stream.clear();
После этого можно выполнять операции обмена — опять до очередной ошибки. Метод clear() перегружен и позволяет не только сбросить, но и затем установить нужные флаги состояния. Прототип этого метода
void clear(iostate flag);
Состояние bad – это более тяжелое состояние. Флаг badbit указывает на неработоспособность потока данных или потерю данных. Когда поток в состоянии bad, ни в чем нельзя быть уверенным, поэтому в таких случаях лучше завершать выполнение программы.
Состояние eof может возникнуть только при операции чтения. Для стандартного потока ввода это состояние возникает при вводе комбинации клавиш <Ctrl> + <z>. Для файлов это состояние устанавливается при первой попытке чтения после последнего байта файла. При этом поток тоже переводится в состояние fail.
Состояние потока и логические условия
Состояние потока можно проверять непосредственно в условиях if и while. Например, ввод массива целых значений можно выполнять в цикле таким образом
int aa[10] = {0}; i = 0; while(cin >> aa[i++]);
Цикл завершается по одной из двух причин:
1. Создалась ситуация «end-of-file»; для входного потока это эквивалентно нажатию комбинации клавиш <Ctrl> + <z>;
2. В потоке встретился символ, не соответствующий типу вводимой переменной; в этом случае поток переводится в состояние fail;
Например, мы набрали на клавиатуре следующую последовательность символов
1<пробел>2<пробел>3<пробел>4<пробел>5e6<enter>
Тогда элементы массива а будут иметь следующие значения:
aa[0] = 1; aa[1] = 2; aa[2] = 3; aa[3] = 4; aa[4] = 5; aa[5] = 0; aa[6] = 0; aa[7] = 0; aa[8] = 0; aa[9] = 0;
Символ ‘e’после цифры 5 не является допустимым для целого числа, поэтому ограничивает ввод.
В условиях можно задавать и явный вызов методов. Например, мы хотим посчитать количество символов ‘\n’, которым оканчиваются вводимые строки. Это можно сделать, используя метод get():
int nl = 0; while(cin.get(ch)) // читать все символы, в том числе пробельные { if(ch=='\n') nl++; cout.put(ch); }
Так как «неправильных» символов не существует, цикл можно завершить только вводом комбинации <Ctrl> + <z>. Можно использовать и другую форму метода get():
int nl = 0; while(ch = cin.get() && ch != EOF) // читать все символы { if(ch=='\n') nl++; cout.put(ch); }
Константа EOF помещается в ch при нажатии комбинации <Ctrl> + <z>.
В библиотеке реализована логическая операция operator!, с помощью которой можно проверить аварийное состояние потока, например
if (!(cin >> x)) { // ввод завершился неудачей . . . }
Обратите внимание, выражение ввода заключено в скобки. Это делать необходимо, так как в соответствии с приоритетом операций выражение без скобок интерпретируется как
(!cin) >> x
Естественно, ввод и проверку состояния потока можно разделить, например
cin >> x; if(!cin) { // ввод завершился неудачей . . . }
Во всех приведенных примерах, на месте cin может стоять любой поток.
При разделении ввода и проверки можно использовать явный вызов соответствующего метода. Например, таким способом мы можем проверять наступление ситуации «end-of-file» для потока, связанного с файлом.
while (!from.eof()) // явная проверка на конец файла { getline(from, s); // чтение строки из потока from cout << s << endl; }
Однако тут возможны сюрпризы. По умолчанию ограничителем строки является символ ‘\n’, который записан в конце каждой строки. В данном случае при считывании последней строки ситуации конца файла не возникает — она возникает только при попытке ввода за концом файла. Поэтому цикл выполнится лишний раз и на экране дважды появится последняя строка.
Правильная последовательность операторов может быть такой
getline(from, s); // чтение строки из потока from while (!from.eof()) // явная проверка на конец файла { cout << s << endl; getline(from, s); // чтение строки из потока from }
Другой вариант — бесконечный цикл с проверкой «end-of-file» внутри цикла
while (true) { getline(from, s); // чтение строки из потока from if(from.eof()) break; // явная проверка на конец файла cout << s << endl; }