образом и применяется. Помимо этого существует также несколько
форматирующих функций, создающих представление своего параметра в
виде строки, которая используется для вывода. Их второй
(необязательный) параметр указывает, сколько символьных позиций
должно использоваться.
char* oct(long, int =0); // восьмеричное представление
char* dec(long, int =0); // десятичное представление
char* hex(long, int =0); // шестнадцатиричное представление
char* chr(int, int =0); // символ
char* str(char*, int =0); // строка
Если не задано поле нулевой длины, то будет производиться усечение
или дополнение; иначе будет использоваться столько символов
(ровно), сколько нужно. Например:
cout << "dec(" << x
<< ") = oct(" << oct(x,6)
<< ") = hex(" << hex(x,4)
<< ")";
Если x==15, то в результате получится:
dec(15) = oct( 17) = hex( f);
Можно также использовать строку в общем формате:
- стр 238 -
char* form(char* format ...);
cout<
8.2.5 Виртуальная Функция Вывода
Иногда функция вывода должна быть virtual. Рассмотрим пример
класса shape, который дает понятие фигуры (#1.18):
class shape {
// ...
public:
// ...
virtual void draw(ostream& s); // рисует "this" на "s"
};
class circle : public shape {
int radius;
public:
// ...
void draw(ostream&);
};
То есть, круг имеет все признаки фигуры и может обрабатываться
как фигура, но имеет также и некоторые специальные свойства,
которые должны учитываться при его обработке.
Чтобы поддерживать для таких классов стандартную парадигму
вывода, операция << определяется так:
ostream& operator<<(ostream& s, shape* p)
{
p->draw(s);
return s;
}
- стр 241 -
Если next - итератор типа определенного в #7.3.3, то список фигур
распечатывается например так:
while ( p = next() ) cout << p;
8.3 Файлы и Потоки
Потоки обычно связаны с файлами. Библиотека потоков создает
стандартный поток ввода cin, стандартный поток вывода cout и
стандартный поток ошибок cerr. Программист может открывать другие
файлы и создавать для них потоки.
8.3.1 Инициализация Потоков Вывода
ostream имеет конструкторы:
class ostream {
// ...
ostream(streambuf* s); // связывает с буфером потока
ostream(int fd); // связывание для файла
ostream(int size, char* p); // связывет с вектором
};
Главная работа этих конструкторов - связывать с потоком буфер.
streambuf - класс, управляющий буферами; он описывается в #8.6, как
и класс filebuf, управляющий streambuf для файла. Класс filebuf
является производным от класса streambuf.
Описание стандартных потоков вывода cout и cerr, которое
находится в исходных кодах баблиотеки потоков ввода/вывода,
выглядит так:
// описать подходящее пространство буфера
char cout_buf[BUFSIZE]
// сделать "filebuf" для управления этим пространством
// связать его с UNIX'овским потоком вывода 1 (уже открытым)
filebuf cout_file(1,cout_buf,BUFSIZE);
// сделать ostream, обеспечивая пользовательский интерфейс
ostream cout(&cout_file);
char cerr_buf[1];
// длина 0, то есть, небуферизванный
// UNIX'овский поток вывода 2 (уже открытый)
filebuf cerr_file()2,cerr_buf,0;
ostream cerr(&cerr_file);
Примеры двух других конструкторов ostream можно найти в #8.3.3 и
#8.5.
- стр 242 -
8.3.2 Закрытие Потоков Вывода
Деструктор для ostream сбрасывает буфер с помощью открытого члена
функции ostream::flush():
ostream::~ostream()
{
flush(); // сброс
}
Сбросить буфер можно также и явно. Например:
cout.flush();
8.3.3 Открытие Файлов
Точные детали того, как открываются и закрываются файлы,
различаются в разных операционных системах и здесь подробно не
описываются. Поскольку после включения становятся
доступны cin, cout и cerr, во многих (если не во всех) программах
не нужно держать код для открытия файлов. Вот, однако, программа,
которая открывает два файла, заданные как параметры командной
строки, и копирует первый во второй:
#include
void error(char* s, char* s2)
{
cerr << s << " " << s2 << "\n";
exit(1);
}
main(int argc, char* argv[])
{
if (argc != 3) error("неверное число параметров","");
filebuf f1;
if (f1.open(argv[1],input) == 0)
error("не могу открыть входной файл",argv[1]);
istream from(&f1);
filebuf f2;
if (f2.open(argv[2],output) == 0)
error("не могу создать выходной файл",argv[2]);
ostream to(&f2);
char ch;
while (from.get(ch)) to.put(ch);
if (!from.eof() !! to.bad())
error("случилось нечто странное","");
}
- стр 243 -
Последовательность действий при создании ostream для именованного
файла та же, что используется для стандартных потоков: (1) сначала
создается буфер (здесь это делается посредством описания filebuf);
(2) затем к нему подсоединяется файл (здесь это делается
посредством открытия файла с помощью функции filebuf::open()); и,
накрнец, (3) создается сам ostream с filebuf в качестве параметра.
Потоки ввода обрабатываются аналогично.
Файл может открываться в одной из двух мод:
enum open_mode { input, output };
Действие filebuf::open() возвращает 0, если не может открыть файл в
соответствие с требованием. Если пользователь пытается открыть
файл, которого не существует для output, он будет создан.
Перед завершением программа проверяет, находятся ли потоки в
приемлемом состоянии (см. #8.4.2). При завершении программы
открытые файлы неявно закрываются.
Файл можно также открыть одновременно для чтения и записи, но в
тех случаях, когда это оказывается необходимо, парадигма потоков
редко оказывается идеальной. Часто лучше рассматривать такой файл
как вектор (гигантских размеров). Можно определить тип, котоырй
позволяет программе обрабатывать файл как вектор; см. Упражнения 8-
10.
8.3.4 Копирование Потоков
Есть возможность копировать потоки. Например:
cout = cerr;
В результате этого получаются две переменные, ссылающиеся на один и
тот же поток. Гавным образом это бывает полезно для того, чтобы
сделать стандартное имя вроде cin ввылающимся на что-то другое
(пример этого см. в #3.1.6)
8.4 Ввод
Ввод аналогичен выводу. Имеется класс istream, который
предоставляет операцию >> ("взять из") для небольшого множества
стандартных типов. Функция operator>> может определяться для типа,
определяемого пользователем.
8.4.1 Ввод Встроенных Типов
Класс istream определяется так:
- стр 244 -
class istream {
// ...
public:
istream& operator>>(char*); // строка
istream& operator>>(char&); // символ
istream& operator>>(short&);
istream& operator>>(int&);
istream& operator>>(long&);
istream& operator>>(float&);
istream& operator>>(double&);
// ...
};
Функции ввода определяются в таком духе:
istream& istream::operator>>(char& c);
{
// пропускает пропуски
int a;
// неким образом читает символ в "a"
c = a;
}
Пропуск определяется как стандартнчй пропуск в C, через вызов
isspase() в том виде, как она определена в (пробел,
табуляция, символ новой строки, перевод формата и возврат каретки).
В качестве альтернативы можно использовать функции get():
class istream {
// ...
istream& get(char& c); // char
istream& get(char* p, int n, int ='\n'); // строка
};
Они обрабатывают символы пропуска так же, как остальные символы.
Функция istream::get(char) читает один и тот же символ в свой
параметр; другая istream::get читает не более n символов в вектор
символов, начинающийся в p. Необязательный третий параметр
используется для задания символа остановки (иначе, терминатора или
ограничителя), то есть этот символ читаться не будет. Если будет
встречен символ ограничитель, он останется как первый символ
потока. По умолчанию вторая функция get будет читать самое большее
n символов, но не больше чем одну строку, '\n' является
ограничителем по умолчанию. Необязательный третий параметр задает
символ, который читаться не будет. Например:
cin.get(buf,256,'\t');
будет читать в buf не более 256 символов, а если встретится
табуляция ('\t'), то это приведет к возврату из get. В этом случае
следующим символом, который будет считан из cin, будет '\t'.
Стандартный заголовочный файл определяет несколько
функций, которые могут оказаться полезными при осуществлении ввода:
- стр 245 -
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 &&c<=127; }
Все кроме isascii() реализуются внешне одинаково, с применением
символа в качестве индекса в таблице атрибутов символов. Поэтому
такие выражения, как
(('a'<=c && c<='z') || ('A'<=c && 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
может быть все что угодно.
Состояние потока можно проверять например так:
- стр 246 -
switch (cin.rdstate()) {
case _good:
// последняя операция над cin прошла успешно
break;
case _eof:
// конец файла
break;
case _fail:
// некоего рода ошибка форматирования
// возможно, не слишком плохая
break;
case _bad:
// возможно, символы cin потеряны
break;