C++
Шрифт:
Функции ввода определяются в таком духе:
istream amp; istream::operator»»(char amp; c); (* // пропускает пропуски int a; // неким образом читает символ в "a" c = a; *)
Пропуск определяется как стандартнчй пропуск в C, через вызов isspase в том виде, как она определена в «ctype.h» (пробел, табуляция, символ новой строки, перевод формата и возврат каретки).
В качестве альтернативы можно использовать функции get:
class istream (* // ... istream amp; get(char amp; c); // char istream amp; get(char* p, int n, int ='\n'); // строка *);
Они обрабатывают символы пропуска так же, как остальные символы. Функция istream::get(char) читает один символ в свой параметр; другая istream::get
cin.get(buf,256,'\t');
будет читать в buf не более 256 символов, а если встртится табуляция ('\t'), то это приведет к возврату из get. В этом случае следующим символом, который будет считан из cin, будет '\t'.
Стандартный заголовочный файл «ctype.h» определяет неколько функций, которые могут оказаться полезными при осществлении ввода:
int isalpha(char) // 'a'..'z' 'A'..'Z' int isupper(char) // 'A'..'Z' int islower(char) // 'a'..'z' int isdigit(char) // '0'..'9' int isxdigit(char) // '0'..'9' 'a'..'f' 'A'..'F' int isspase(char) // ' ' '\t' возврат новая строка // перевод формата int iscntrl(char) // управляющий символ // (ASCII 0..31 и 127) int ispunct(char) // пунктуация:ни один из вышеперечисленных int isalnum(char) // isalpha ! isdigit int isprint(char) // печатаемый: ascii ' '..'-' int isgraph(char) // isalpha ! isdigit ! ispunct int isascii(char c) (* return 0«=c amp; amp; c«=127; *)
Все кроме isascii реализуются внешне одинаково, с прменением символа в качестве индекса в таблице атрибутов символов. Поэтому такие выражения, как
(('a'«=c amp; amp; c«='z') !! ('A'«=c amp; amp; c«='Z')) // алфавитный
не только утомительно пишутся и чреваты ошибками (на мшине с набором символов EBCDIC оно будет принимать неалфавиные символы), они также и менее эффективны, чем применение стандартной функции:
isalpha(c)
8.4.2 Состояния потока
Каждый поток (istream или ostream) имеет ассоциированное с ним состояние, и обработка ошибок и нестандартных условий осуществляется с помощью соответствующей установки и проверки этого состояния.
Поток может находиться в одном из следующих состояний:
enum stream_state (* _good, _eof, _fail, _bad *);
Если состояние _good или _eof, значит последняя операция ввода прошла успешно. Если состояние _good, то следующая опрация ввода может пройти успешно, в противном случае она зкончится неудачей. Другими словами, применение операции ввода к потоку, который не находится в состоянии _good, является пустой операцией. Если делается попытка читать в переменную v, и операция оканчивается неудачей, значение v должно отаться неизменным (оно будет неизменным, если v имеет один из тех типов, которые обрабатываются функциями членами istream или ostream). Отличие между состояниями _fail и _bad очень незначительно и предсавляет интерес только для разработчиков операций ввода. В состоянии _fail предполагается, что поток не испорчен и никакие символы не потеряны. В состоянии _bad может быть все что угодно.
Состояние потока можно проверять например так:
switch (cin.rdstate) (* case _good: // последняя операция над cin прошла успешно break; case _eof: // конец файла break; case _fail: // некоего рода ошибка форматирования // возможно, не слишком плохая break; case _bad: //
возможно, символы cin потеряны break; *)Для любой переменной z типа, для которого определены операции «„ и “», копирующий цикл можно написать так:
while (cin»»z) cout «„ z «« «\n“;
Например, если z – вектор символов, этот цикл будет брать стандартный ввод и помещать его в стандартный вывод по одному слову (то есть, последовательности символов без пробла) на строку.
Когда в качестве условия используется поток, происходит проверка состояния потока, и эта проверка проходит успешно (то есть, значение условия не ноль) только если состояние _good. В частности, в предыдущем цикле проверялось состояние istream, которое возвращает cin»»z. Чтобы обнаружить, почему цикл или проверка закончились неудачно, можно исследовать состояние. Такая проверка потока реализуется операцией преоразования (#6.3.2).
Делать проверку на наличие ошибок после каждого ввода или вывода действительно не очень удобно, и обычно источником ошибок служит программист, не сделавший этого в том месте, где это существенно. Например, операции вывода обычно не проверяются, но они могут случайно не сработать. Парадигма потка ввода/вывода построена так, чтобы когда в С++ появится (если это произойдет) механизм обработки исключительных ситаций (как средство языка или как стандартная библиотека), его будет легко применить для упрощения и стандартизации обрабоки ошибок в потоках ввода/вывода.
8.4.3 Ввод типов, определяемых пользователем
Ввод для пользовательского типа может определяться точно так же, как вывод, за тем исключением, что для операции ввода важно, чтобы второй параметр был ссылочного типа. Например:
istream amp; operator»»(istream amp; s, complex amp; a) /* форматы ввода для complex; "f" обозначает float: f ( f ) ( f , f ) */ (* double re = 0, im = 0; char c = 0;
s »» c; if (c == '(') (* s »» re »» c; if (c == ',') s »» im »» c; if (c != ')') s.clear(_bad); // установить state *) else (* s.putback(c); s »» re; *)
if (s) a = complex(re,im); return s; *)
Несмотря на то, что не хватает кода обработки ошибок, большую часть видов ошибок это на самом деле обрабатывать бдет. Локальная переменная c инициализируется, чтобы ее значние не оказалось случайно '(' после того, как операция окочится неудачно. Завершающая проверка состояния потока гарантирует, что значение параметра a будет изменяться только в том случае, если все идет хорошо.
Операция установки состояния названа clear (очистить), потому что она чаще всего используется для установки состония потока заново как _good. _good является значением парметра по умолчанию и для istream::clear, и для ostream::clear.
Над операциями ввода надо поработать еще. Было бы, в частности, замечательно, если бы можно было задавать ввод в терминах образца (как в языках Snobol и Icon), а потом проврять, прошла ли успешно вся операция ввода. Такие операции должны были бы, конечно, обеспечивать некоторую дополнителную буферизацию, чтобы они могли воссанавливать поток ввода в его исходное состояние после неудачной попытки распознавания.
8.4.4 Инициализация потоков ввода
Естественно, тип istream, так же как и ostream, снабжен конструктором:
class istream (*
// ... istream(streambuf* s, int sk =1, ostream* t =0); istream(int size, char* p, int sk =1); istream(int fd, int sk =1, ostream* t =0); *);
Параметр sk задает, должны пропускаться пропуски или нет. Параметр t (необязательный) задает указатель на ostream, к которому прикреплен istream. Например, cin прикреплен к cout; это значит, что перед тем, как попытаться читать симвлы из своего файла, cin выполняет