Шаг 15.
Выходные строковые потоки

    На этом шаге мы рассмотрим создание и использование выходных строковых потоков.

    Выходные строковые потоки обычно создаются с помощью такого конструктора класса ostrstream:

    ostrstream имя_потока(char *str, int len, int mode);
Необязательное имя_потока - это идентификатор, произвольно выбираемый программистом. Указатель str должен адресовать уже существующий участок памяти. Параметр int len определяет размеры этого участка (буфера). Последний параметр - индикатор режима обмена mode. Режим обмена строкового потока при выводе определяет размещение информации в связанной с потоком строке. Для задания конкретного режима используется флаг или дизъюнкция нескольких флагов:
ios::out
строковый поток создается для вывода, запись информации ведется с начала строки;
ios::ate
позиция записи устанавливается в месте размещения нулевого признака конца строки ' \0' (запись в продолжение);
ios::арр
для строковых типов этот флаг действует аналогично флагу ios::ate, но в конкретных реализациях могут быть отличия, обычно отражаемые в документации.

    Вместе с флагом ios::out могут быть указаны (в дизъюнктивной форме) флаги ios::ate или ios::арр. В обоих случаях при формировании потока позиция записи устанавливается на нулевой признак '\0' конца строки (буфера потока), т.е. предполагается запись в конец потока.

    По умолчанию выбирается ios::out, т.е. строковый поток создается для вывода (записи) информации с начала строки (буфера).

    Начнем рассмотрение основных возможностей выходных строковых потоков с их безымянного варианта. В следующей программе значение строки обязательного аргумента функции main с помощью операции вставки << переносится в безымянный строковый поток, связанный с символьным массивом path[]:

//OOР15_1.СРР - запись в безымянный выходной строковый поток (<<).
#include <strstrea.h>
void main(int Narg,  char  *arg[])
{
  char path [80] ;
  ostrstream(path,sizeof(path))  << arg[0] <<  '\0';
  cout<< "\nПолное имя программы:   "  << path << endl;
}
Текст этой программы можно взять здесь.

    Результат выполнения программы:

    Полное имя программы:   D:\WWP\TESTPROG\OOP52_1.EXE

    Так как операция включения << не переносит в выходной поток признак конца строки '\0', то его пришлось явным образом поместить в выходной поток (тем самым в буфер path[]).

    Функция write() применительно к выходному потоку позволяет записывать в него данные без форматирования, т.е. строка записывается вместе с пробельными символами и символом конца строки '\0'. Чтобы продемонстрировать особенности ее применения к безымянным выходным строковым потокам, рассмотрим следующую программу:

//OOР15_2.СРР - запись в безымянный выходной строковый
//  поток; копирование строки с использованием 	функции write().
#include <strstrea.h> 
void main ()
{
  char lat[] = "Quod erat demonstrandum!"; 
  char rus[] = " - Что и требовалось доказать!";
  char result[60];
  ostrstream(result, sizeof (result)) .write (lat, sizeof (lat));
  ostrstream(result, sizeof (result) , ios::ate) .write (rus, sizeof (rus)); 
  cout  <<  "\n"  << result  <<  endl; 
}
Текст этой программы можно взять здесь.

    Результат выполнения программы:

    Quod erat demonstrandum!   -  Что и требовалось  доказать!

    В программе два безымянных потока, каждый из которых "настроен" на символьный массив result[]. При создании первого безымянного потока в качестве параметров конструктора указываются - массив result[] и его размеры, т.е. длина массива в байтах. Функция write() "присоединяется" с помощью операции "точка" непосредственно к конструктору и тем самым вызывается для созданного им безымянного потока. В качестве фактических параметров функции write() используются указатель lat на строку и количество записываемых символов. Так как длина строки меньше длины буфера result, то буфер не заполняется целиком. При создании второго безымянного потока, кроме буфера result и его длины, в конструкторе (в качестве третьего параметра) использован флаг ios::ate, под действием которого поток создается как "дополняемый". Тем самым последующая запись в поток выполняется не с начала потока, а начиная с позиции окончания предыдущей записи '\0'. Именно тут функция write() помещает строку, адресованную указателем rus. Та самым в массиве result[] осуществляется конкатенация строк, что видно из результатов. Из массива result [] вывод в поток cout выполняется до появления символа конца строки ' \0' в массиве result.

    Следующая программа иллюстрирует особенности последовательного вывода в строковый поток данных разных типов с помощью операции включения:

//OOР15_3.СРР - вывод в строковый поток операцией <<.
#include <strstrea.h>
void main()
{ 
  char buffer[180];
  ostrstream outstring(buffer, sizeof(buffer), ios::out | ios::ate);
  outstring << "\nБез явного включения разделителей" 
                   << " текст в потоке\n\"сливается\":\n ";
  outstring << 123456789 << -456 << +1.23456789;
  outstring << -0.123456789e+1 << +123.456789e-3 << ends;
  cout << "\n" << buffer << endl; 
}
Текст этой программы можно взять здесь.

    Результат выполнения программы:

    Без явного включения разделителей текст в потоке
    "сливается":
    123456789-4561.23456789-1.23456780.123457

    Как показывают результаты, последовательное обращение к строковому потоку приводит к записи "в продолжение", т.е. указатель позиции записи при создании потока устанавливается на его начало, а затем перемещается на длину каждой новой записи. Никаких промежутков или разделителей между выводимыми данными не добавляется. Более того, операция включения в поток << даже не переносит в него нулевой ограничитель конца строки '\0'. Поэтому его нужно добавлять явно, если в дальнейшем требуется использовать буфер потока в качестве строки. Числовая информация, включаемая в поток операцией <<, форматируется. При этом знак + для чисел заменяется пробелом, вещественные числа, заданные в экспоненциальной форме (в научной нотации), переводятся в форму с фиксированной точкой. Выполняется округление дробной части вещественного числа. Перечисленные и проиллюстрированные результатами особенности форматирования могут быть изменены с помощью средств управления форматом как и для стандартных потоков.

    На следующем шаге мы рассмотрим двунаправленные строковые потоки.




Предыдущий шаг Содержание Следующий шаг