Б. Страуструп - Язык программирования С++ (1119446), страница 73
Текст из файла (страница 73)
Суть ее втом, что создается объект, который можно передавать куда угодно и который используется как функция.Передача объекта является более гибким решением, поскольку детали выполнения частичноопределяются создателем объекта, а частично тем, кто к нему обращается.10.4.2.1 Стандартные манипуляторы ввода-выводаЭто следующие манипуляторы:// Simple manipulators:ios& oct(ios&); // в восьмеричной записиios& dec(ios&); // в десятичной записиios& hex(ios&); // в шестнадцатеричной записиostream& endl(ostream&);// добавить '\n' и вывести273Бьерн Страуструп.Язык программирования С++ostream& ends(ostream&);ostream& flush(ostream&);istream& ws(istream&);// добавить '\0' и вывести// выдать поток// удалить обобщенные пробелы// Манипуляторы имеют параметры:SMANIP<int>SMANIP<int>SMANIP<int>SMANIP<int>SMANIP<long>SMANIP<long>setbase(int b);setfill(int f);setprecision(int p);setw(int w);resetiosflags(long b);setiosflags(long b);Например,cout << 1234 << ' '<< hex << 1234 << ' '<< oct << 1234 << endl;напечатает1234 4d2 2322иcout << setw(4) << setfill('#') << '(' << 12 << ")\n";cout << '(' << 12 << ")\n";напечатает(##12)(12)Не забудьте включить файл <iomanip.h>, если используете манипуляторы с параметрами.10.4.3 Члены ostreamВ классе ostream есть лишь несколько функций для управления выводом, большая часть таких функцийнаходится в классе ios.class ostream : public virtual ios {//...public:ostream& flush();ostream& seekp(streampos);ostream& seekp(streamoff, seek_dir);streampos tellp();//...};Как мы уже говорили, функция flush() опустошает буфер в выходной поток.
Остальные функциииспользуются для позиционирования в ostream при записи. Окончание на букву p указывает, что именнопозиция используется при выдаче символов в заданный поток. Конечно эти функции имеют смысл,только если поток присоединен к чему-либо, что допускает позиционирование, например файл. Типstreampos представляет позицию символа в файле, а тип streamoff представляет смещениеотносительно позиции, заданной seek_dir. Все они определены в классе ios:class ios {//...enum seek_dir {beg=0,cur=1,end=2};// от начала файла// от текущей позиции в файле// от конца файла274Бьерн Страуструп.Язык программирования С++//...};Позиции в потоке отсчитываются от 0, как если бы файл был массивом из n символов:char file[n-1];и если fout присоединено к file, тоfout.seek(10);fout<<'#';поместит # в file[10].10.4.4 Члены istreamКак и для ostream, большинство функций форматирования и управления вводом находится не в классеiostream, а в базовом классе ios.class istream : public virtual ios {//...public:intpeek()istream&putback(char c);istream&seekg(streampos);istream&seekg(streamoff, seek_dir);streampostellg();//...};Функции позиционирования работают как и их двойники из ostream.
Окончание на букву g показывает,что именно позиция используется при вводе символов из заданного потока. Буквы p и g нужны,поскольку мы можем создать производный класс iostreams из классов ostream и istream, и в немнеобходимо следить за позициями ввода и вывода.С помощью функции peek() программа может узнать следующий символ, подлежащий вводу, незатрагивая результата последующего чтения. С помощью функции putback(), как показано в $$10.3.3,можно вернуть ненужный символ назад в поток, чтобы он был прочитан в другое время.10.5 Файлы и потокиНиже приведена программа копирования одного файла в другой. Имена файлов берутся из команднойстроки программы:#include <fstream.h>#include <libc.h>void error(char* s, char* s2 =""){cerr << s << ' ' << s2 << '\n';exit(1);}int main(int argc, char* argv[]){if (argc != 3) error("wrong number of arguments");ifstream from(argv[1]);if (!from) error("cannot open input file",argv[1]);ostream to(argv[2]);if (!to) error("cannot open output file",argv[2]);char ch;while (from.get(ch)) to.put(ch);275Бьерн Страуструп.Язык программирования С++if (!from.eof() || to.bad())error("something strange happened");return 0;}Для открытия выходного файла создается объект класса ofstream - выходной поток файла,использующий в качестве аргумента имя файла.
Аналогично, для открытия входного файла создаетсяобъект класса ifstream - входной файловый поток, также использующий в качестве аргумента имяфайла. В обоих случаях следует проверить состояние созданного объекта, чтобы убедиться вуспешном открытии файла, а если это не так, операции завершатся не успешно, но корректно.По умолчанию ifstream всегда открывается на чтение, а ofstream открывается на запись. В ostream и вistream можно использовать необязательный второй аргумент, указывающий иные режимы открытия:class ios {public://...enum open_mode {in=1,out=2,ate=4,app=010,trunc=020,nocreate=040,noreplace=0100};//...};//////////////открыть на чтениеоткрыть как выходнойоткрыть и переместиться в конец файладобавитьсократить файл до нулевой длинынеудача, если файл не существуетнеудача, если файл существуетНастоящие значения для open_mode и их смысл вероятно будут зависеть от реализации.
Будьте добры,за деталями обратитесь к руководству по вашей библиотеке или экспериментируйте. Приведенныекомментарии могут прояснить их назначение. Например, можно открыть файл с условием, что операцияоткрытия не выполнится, если файл уже не существует:void f(){ofstream mystream(name,ios::out|ios::nocreate);if (ofstream.bad()) {//...}//...}Также можно открыть файл сразу на чтение и запись:fstream dictionary("concordance", ios::in|ios::out);Все операции, допустимые для ostream и ostream, можно применять к fstream.
На самом деле, классfstream является производным от iostream, который является, в свою очередь, производным от istream иostream. Причина, по которой информация по буферизации и форматированию для ostream и istreamнаходится в виртуальном базовом классе ios, в том, чтобы заставить действовать всю этупоследовательность производных классов. По этой же причине операции позиционирования в istream иostream имеют разные имена - seekp() и seekg().
В iostream есть отдельные позиции для чтения изаписи.10.5.1 Закрытие потоковФайл может быть закрыт явно, если вызвать close() для его потока:mystream.close();Но это неявно делает деструктор потока, так что явный вызов close() может понадобиться, если толькофайл нужно закрыть до достижения конца области определенности потока.276Бьерн Страуструп.Язык программирования С++Здесь возникает вопрос, как реализация может обеспечить создание предопределенных потоков cout,cin и cerr до их первого использования и закрытие их только после последнего использования. Конечно,разные реализации библиотеки потоков из <iostream.h> могут по-разному решать эту задачу.
В концеконцов, решение – это прерогатива реализации, и оно должно быть скрыто от пользователя. Здесьприводится только один способ, примененный только в одной реализации, но он достаточно общий,чтобы гарантировать правильный порядок создания и уничтожения глобальных объектов различныхтипов.Основная идея в том, чтобы определить вспомогательный класс, который по сути служит счетчиком,следящим за тем, сколько раз <iostream.h> был включен в раздельно компилировавшиеся программныефайлы:class Io_init {static int count;//...public:Io_init();^Io_init();};static Io_init io_init ;Для каждого программного файла определен свой объект с именем io_init.
Конструктор для объектовio_init использует Io_init::count как первый признак того, что действительная инициализация глобальныхобъектов потоковой библиотеки ввода-вывода сделана в точности один раз:Io_init::Io_init(){if (count++ == 0) {// инициализировать cout// инициализировать cerr// инициализировать cin// и т.д.}}Обратно, деструктор для объектов io_init использует Io_count, как последнее указание на то, что всепотоки закрыты:Io_init::^Io_init(){if (--count == 0) {// очистить cout (сброс, и т.д.)// очистить cerr (сброс, и т.д.)// очистить cin// и т.д.}}Это общий прием работы с библиотеками, требующими инициализации и удаления глобальныхобъектов.
Впервые в С++ его применил Д. Шварц. В системах, где при выполнении все программыразмещаются в основной памяти, для этого приема нет помех. Если это не так, то накладные расходы,связанные с вызовом в память каждого программного файла для выполнения функций инициализации,будут заметны. Как всегда, лучше, по возможности, избегать глобальных объектов. Для классов, вкоторых каждая операция значительна по объему выполняемой работы, чтобы гарантироватьинициализацию, было бы разумно проверять такие первые признаки (наподобие Io_init::count) прикаждой операции. Однако, для потоков такой подход был бы излишне расточительным.10.5.2 Строковые потокиКак было показано, поток может быть привязан к файлу, т.е.
массиву символов, хранящемуся не восновной памяти, а, например, на диске. Точно так же поток можно привязать к массиву символов в277Бьерн Страуструп.Язык программирования С++основной памяти. Например, можно воспользоваться выходным строковым потоком ostrstream дляформатирования сообщений, не подлежащих немедленной печати:char* p = new char[message_size];ostrstream ost(p,message_size);do_something(arguments,ost);display(p);С помощью стандартных операций вывода функция do_something может писать в поток ost, передаватьost подчиняющимся ей функциям и т.п.