straustrup2 (852740), страница 68
Текст из файла (страница 68)
Поэтому вместо выражения типа(('a'<=c && c<='z') || ('A'<=c && c<='Z')) // буквакоторое не только утомительно писать, но оно может быть и ошибочным (на машине с кодировкойEBCDIC оно задает не только буквы), лучше использовать вызов стандартной функции isalpha(),который к тому же более эффективен. В качестве примера приведем функцию eatwhite(), которая читаетиз потока обобщенные пробелы:istream& eatwhite(istream& is){char c;while (is.get(c)) {if (isspace(c)==0) {is.putback(c);break;}}return is;}В ней используется функция putback(), которая возвращает символ в поток, и он становится первымподлежащим чтению.10.3.2 Состояния потокаС каждым потоком (istream или ostream) связано определенное состояние.
Нестандартные ситуации иошибки обрабатываются с помощью проверки и установки состояния подходящим образом. Узнатьсостояние потока можно с помощью операций над классом ios:class ios {//ios является базовым для ostream и istream//...public:int eof() const;// дошли до конца файлаint fail() const;// следующая операция будет неудачнаint bad() const;// поток испорченint good() const;// следующая операция будет успешной//...};Последняя операция ввода считается успешной, если состояние задается good() или eof(). Еслисостояние задается good(), то последующая операция ввода может быть успешной, в противном случаеона будет неудачной.
Применение операции ввода к потоку в состоянии, задаваемом не good(),считается пустой операцией. Если произошла неудача при попытке чтения в переменную v, то значениеv не изменилось (оно не изменится, если v имеет тип, управляемый функциями члена из istream или263Бьерн Страуструп.Язык программирования С++ostream).
Различие между состояниями, задаваемыми как fail() или как bad() уловить трудно, и оноимеет смысл только для разработчиков операций ввода. Если состояние есть fail(), то считается, чтопоток не поврежден, и никакие символы не пропали; о состоянии bad() ничего сказать нельзя.Значения, обозначающие эти состояния, определены в классе ios:class ios {//...public:enum io_state {goodbit=0,eofbit=1,filebit=2,badbit=4,};//...};Истинные значения состояний зависят от реализации, и указанные значения приведены только, чтобыизбежать синтаксически неправильных конструкций.Проверять состояние потока можно следующим образом:switch (cin.rdstate()) {case ios::goodbit:// последняя операция с cin была успешнойbreak;case ios::eofbit:// в конце файлаbreak;case ios::filebit:// некоторый анализ ошибки// возможно неплохойbreak;case ios::badbit:// cin возможно испорченbreak;}В более ранних реализациях для значений состояний использовались глобальные имена.
Этоприводило к нежелательному засорению пространства именования, поэтому новые имена доступнытолько в пределах класса ios. Если вам необходимо использовать старые имена в сочетании с новойбиблиотекой, можно воспользоваться следующими определениями:const int _good = ios::goodbit;const int _bad = ios::badbit;const int _file = ios::filebit;const int _eof = ios::eofbit;typedef ios::io_state state_value ;Разработчики библиотек должны заботится о том, чтобы не добавлять новых имен к глобальномупространству именования. Если элементы перечисления входят в общий интерфейс библиотеки, онивсегда должны использоваться в классе с префиксами, например, как ios::goodbit и ios::io_state.Для переменной любого типа, для которого определены операции << и >>, цикл копированиязаписывается следующим образом:while (cin>>z) cout << z << '\n';Если поток появляется в условии, то проверяется состояние потока, и условие выполняется (т.е.результат его не 0) только для состояния good().
Как раз в приведенном выше цикле проверяетсясостояние потока istream, что является результатом операции cin>>z. Чтобы узнать, почему произошланеудача в цикле или условии, надо проверить состояние. Такая проверка для потока реализуется с264Бьерн Страуструп.Язык программирования С++помощью операции приведения (7.3.2).Так, если z является символьным вектором, то в приведенном цикле читается стандартный ввод ивыдается для каждой строки стандартного вывода по одному слову (т.е.
последовательности символов,не являющихся обобщенными пробелами). Если z имеет тип complex, то в этом цикле с помощьюопераций, определенных в 10.2.2 и 10.2.3, будут копироваться комплексные числа. Шаблоннуюфункцию копирования для потоков со значениями произвольного типа можно написать следующимобразом:complex z;iocopy(z,cin,cout);double d;iocopy(d,cin,cout);char c;iocopy(c,cin,cout);// копирование complex// копирование double// копирование charПоскольку надоедает проверять на корректность каждую операцию ввода-вывода, тораспространенным источником ошибок являются именно те места в программе, где такой контрольсущественен. Обычно операции вывода не проверяют, но иногда они могут завершиться неудачно.Потоковый ввод-вывод разрабатывался из того принципа, чтобы сделать исключительные ситуациилегкодоступными, и тем самым упростить обработку ошибок в процессе ввода-вывода.10.3.3 Ввод пользовательских типовОперацию ввода для пользовательского типа можно определить в точности так же, как и операциювывода, но для операции ввода существенно, чтобы второй параметр имел тип ссылки, например:istream& operator>>(istream& s, complex& a)/*формат input рассчитан на complex; "f" обозначает float:f( f )( f , f )*/{double re = 0, im = 0;charc = 0;s >> c;if (c == '(') {s >> re >> c;if (c == ',') s >> im >> c;if (c != ')') s.clear(ios::badbit); // установим состояние}else {s.putback(c);s >> re;}if (s) a = complex(re,im);return s;}Несмотря на сжатость кода, обрабатывающего ошибки, на самом деле учитывается большая частьошибок.
Инициализация локальной переменной с нужна для того, чтобы в нее не попало случайноезначение, например '(', в случае неудачной операции. Последняя проверка состояния потокагарантирует, что параметр a получит значение только при успешном вводе.Операция, устанавливающая состояние потока, названа clear() (здесь clear - ясный, правильный),поскольку чаще всего она используется для восстановления состояния потока как good(); значением поумолчанию для параметра ios::clear() является ios::goodbit.265Бьерн Страуструп.Язык программирования С++10.4 ФорматированиеВсе примеры из 10.2 содержали неформатированный вывод, который являлся преобразованиемобъекта в последовательность символов, задаваемую стандартными правилами, длина которой такжеопределяется этими правилами.
Часто программистам требуются более развитые возможности. Так,возникает потребность контролировать размер памяти, необходимой для операции вывода, и формат,используемый для выдачи чисел. Точно так же допустимо управление некоторыми аспектами ввода.10.4.1 Класс iosБольшинство средств управления вводом-выводом сосредоточены в классе ios, который являетсябазовым для ostream и istream.
По сути здесь находится управление связью между istream или ostreamи буфером, используемым для операций ввода-вывода. Именно класс ios контролирует: как символыпопадают в буфер и как они выбираются оттуда. Так, в классе ios есть член, содержащий информациюоб используемой при чтении или записи целых чисел системы счисления (десятичная, восьмеричнаяили шестнадцатеричная), о точности вещественных чисел и т.п., а также функции для проверки иустановки значений переменных, управляющих потоком.class ios {//...public:ostream* tie(ostream* s);// связать input и outputostream* tie();// возвратить "tie"int width(int w);// установить поле widthint width() const;char fill(char);// установить символ заполненияchar fill() const;// вернуть символ заполненияlong flags(long f);long flags() const;long setf(long setbits, long field);long setf(long);long unsetf(long);int precision(int);// установить точность для floatint precision() const;int rdstate(); const;// состояния потоков, см.
$$10.3.2int eof() const;int fail() const;int bad() const;int good() const;void clear(int i=0);//...};В 10.3.2 описаны функции, работающие с состоянием потока, остальные приведены ниже.10.4.1.1 Связывание потоковФункция tie() может установить и разорвать связь между ostream и istream. Рассмотрим пример:main(){String s;cout << "Password: ";cin >> s;// ...}Как можно гарантировать, что приглашение Password: появится на экране прежде, чем выполнитьсяоперация чтения? Вывод в cout и ввод из cin буферизуются, причем независимо, поэтому Password:появится только по завершении программы, когда закроется буфер вывода.266Бьерн Страуструп.Язык программирования С++Решение состоит в том, чтобы связать cout и cin с помощью операции cin.tie(cout). Если ostream связанс потоком istream, то буфер вывода выдается при каждой операции ввода над istream.
Тогда операцииcout << "Password: ";cin >> s;эквивалентныcout << "Password: ";cout.flush();cin >> s;Обращение is.tie(0) разрывает связь между потоком is и потоком, с которым он был связан, если такойбыл. Подобно другим потоковым функциям, устанавливающим определенное значение, tie(s)возвращает предыдущее значение, т.е.