Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курс ПЯВУ 2 сем / Лекции 2 сем / Л№34.Потоковый ввод / Лекция № 32.Потоковый ввод вывод.odt
Скачиваний:
11
Добавлен:
17.04.2015
Размер:
153.09 Кб
Скачать

Void main()

{

char buffer[180];

char stroka[150], ss[150];

// Строковый поток string связан с массивом buffer:

strstream string (buffer, sizeof (buffer) , ios::in | ios::out);

string << "В строковый поток записывается "

"это предложение." << ends;

// Чтение из строкового потока string в массив stroka:

string.getline(stroka,sizeof(stroka),'\0');

// Вывод в стандартный поток содержимого массива stroka:

cout << "\nstroka = " << stroka;

// Возвращение позиции чтения/записи к началу потока string:

string.seekg(0L, ios::beg) ;

// Чтение иа строкового потока до пробельного символа:

string >> ss;

cout << "\nss = " << ss;

string >> ss;

cout << "\nss = " << ss;

string.getline(ss,sizeof(ss),'\0');

cout << "\nss = " << ss;

}

Текст этой программы можно взять здесь.

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

stroka = В строковый поток записывается это предложение.

ss = В

ss = строковый

ss = поток записывается это предложение.

Обратите внимание, что функция getline() переносит из потока string даже ведущие пробельные символы. Комментарии в тексте программы и результаты ее выполнения достаточно подробно иллюстрируют основные особенности работы с двунаправленными потоками. Отметим только необходимость при операциях << явно занести в поток признак конца строки (манипулятор ends), а при повторном чтении из потока - необходимость перейти к его началу.

Переход к началу потока для чтения из него выполняет функция seekg(), первый параметр которой (типа long) указывает нулевую величину смещения, а второй - положение, от которого это смещение отчитывается. В классе ios определены три возможных начала отсчета:

ios::beg - от начала потока (его буфера);

ios::end - от конца потока;

ios::cur - от текущей позиции чтения/записи.

Обратите внимание, что для двунаправленного потока класса strstream определены два указателя позиций - позиции записи и позиции чтения. Именно поэтому в программе после окончания записи в поток string функция getline() выполняет чтение от его начала, и не требуется перевода указателя чтения (что делает функция seekp()).

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

Строковые потоки можно использовать по-разному. С их помощью в участок памяти, связанный со строковым потоком, можно заносить разнотипную информацию, затем извлекать ее по нужным правилам. Строковый поток можно в этом случае сделать внешним и с его помощью осуществлять межмодульный обмен. В следующей программе с помощью дуального (двунаправленного) строкового потока выполняется обмен между функциями. Строковый поток с именем obmen и связанный с ним символьный массив Link[] определены как глобальные и тем самым доступны во всех функциях файла с программой. В основной программе в строковый поток obmen заносятся данные из массива структур. Предварительно в поток записывается значение количества элементов массива. В функции result() выполняется чтение из строкового потока. При первом обращении к потоку "извлекается" значение количества элементов, используемое затем для организации цикла чтения структур. Структурный тип с именем element и две операции-функции для перегрузки операций обмена >> и << со строковыми потоками определены до функции main(). Текст программы:

//OOР17_1.СРР - перегрузка операций обмена (<<, >>) и

// двунаправленный строковый поток ввода-вывода.

#include <strstrea.h>

const int lenLink = 200; // Глобальная константа.

char Link[lenLink]; // Глобальный символьный массив.

// Строковый поток obmen связан с массивом Link:

strstream obmen (Link,sizeof(Link),ios::in | ios::out);

struct element

{

int nk, nl;

float zn;

};

strstream& operator >> (strstream& in, element& el)

{

in >> el.nk; in >> el.nl; in >> el.zn;

return in;

};

strstream& operator << (strstream& out, element& el)

{

out << ' ' << el.nk << ' ' << el.nl << ' ' << el.zn;

return out;

};

// Функция чтения из потока и вывода на экран:

void result(void)

{

element zuar;

int numb;

obmen >> numb;

cout << "\nnumb = "<< numb;

for(int j = 0; j < numb; j++)

{

obmen >> zuar;

cout << "\nelement[" << j << "] = ";

cout << zuar.nk << '\t' << zuar.nl << '\t' << zuar.zn;

}

}

void main()

{

char buffer[180];

const int numbeEl = 5;

element arel[numbeEl] = { 1, 2, 3.45, 2, 3, 4.56,

22, 11, 45.6, 3, 24, 4.33, 3, 6, -5.3 };

// Запись в строковый поток:

obmen << numbeEl;

for (int i = 0; i < numbeEl; i++)

obmen << arel[i];

obmen << '\0';

result();

}

Текст этой программы можно взять здесь.

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

numb = 5

element[0] = 1 2 3.45

element[1] = 2 3 4.56

element[2] = 22 11 45.599998

element[3] = 3 24 4.33

element[4] = 3 6 -5.3

Со следующего шага мы начнем рассматривать взаимосвязь файлов и потоков.

Потоки для работы с файлами создаются как объекты следующих классов:

ofstream - для вывода (записи) данных в файл;

ifstream - для ввода (чтения) данных из файла;

fstream - для чтения и для записи данных (двунаправленный обмен).

Чтобы использовать эти классы, в текст программы необходимо включить дополнительный заголовочный файл fstream.h. После этого в программе можно определять конкретные файловые потоки, соответствующих типов (объекты классов ofstream, ifstream, fstream), например, таким образом:

ofstream outFile; // Определяется выходной файловый поток.

ifstream inFile; // Определяется входной файловый поток.

fstream ioFile; // Определяется файловый поток для ввода и вывода.

Создание файлового потока (объекта соответствующего класса) связывает имя потока с выделяемым для него буфером и инициализирует переменные состояния потока. Так как перечисленные классы файловых потоков наследуют свойства класса ios, то и переменные состояния каждого файлового потока наследуются из этого базового класса. Так как файловые классы являются производными от классов ostream (класс ofstream), istream (класс ifstream), stream (класс fstream), то они поддерживают описанный в предыдущих шагах форматированный и бесформатный обмен с файлами. Однако прежде чем выполнить обмен, необходимо открыть соответствующий файл и связать его с файловым потоком.

Открытие файла в самом общем смысле означает процедуру, информирующую систему о тех действиях, которые предполагается выполнять с файлом. Существуют функции стандартной библиотеки языка С для открытия файлов fopen(), open(). Но работая с файловыми потоками библиотеки ввода-вывода языка С++, удобнее пользоваться компонентными функциями соответствующих классов.

Создав файловый поток, можно "присоединить" его к конкретному файлу с помощью компонентной функции open(). Функция open() унаследована каждым из файловых классов ofstream, ifsream, fstream от класса fstreambase. С ее помощью можно не только открыть файл, но и связать его с уже определенным потоком. Формат функции:

void open(const char *fileName,

int mode = умалчиваемое_значение,

int protection = умалчиваемое_значение);

Первый параметр - fileName - имя уже существующего или создаваемого заново файла. Это строка, определяющая полное или сокращенное имя файла в формате, регламентированном операционной системой. Второй параметр - mode (режим) - дизъюнкция флагов, определяющих режим работы с открываемым файлом (например, только запись или только чтение). Флаги определены следующим образом:

enum ios::open_mode {

in = 0x01, // Открыть только для чтения.

out = 0x02, // Открыть только для записи.

ate = 0x04, // При открытии искать конец файла.

арр = 0x08, // Дописывать данные в конец файла.

trunc = 0x10, // Вместо существующего создать новый файл.

nocreate = 0x20, // Не открывать новый файл (Для

// несуществующего файла функция open выдаст ошибку).

noreplace = 0x40, // Не открывать существующий файл.

// (Для существующего выходного файла, не имеющего режимов ate

// или арр, выдать ошибку).

binary = 0x80 // Открыть для двоичного (не текстового) обмена.

};

Назначения флагов поясняют комментарии, однако надеяться, что именно такое действие на поток будет оказывать тот или иной флаг в конкретной реализации библиотеки ввода-вывода, нельзя. Даже сам автор языка С++ Б.Страуструп говорит о том, что смысл значений open_mode, скорее всего, будет зависеть от реализации. Например, различие между флагами ios::ate и ios::app проявляется весьма редко, и часто они действуют одинаково. Однако ниже в пояснениях к программе OOР19_3.СРР приведен пример использования флага ios:: арр в конструкторе класса ofstream, где использование ios::ate приведет к ошибке открытия файла. Умалчиваемое значение параметра mode зависит от типа потока, для которого вызывается функция open().

Третий параметр - protection (защита) - определяет защиту и достаточно редко используется. Точнее, он устанавливается по умолчанию и умалчиваемое значение обычно устраивает программиста.

Как обычно вызов функции open() осуществляется с помощью уточненного имени

имя_объекта класса.вызов_принадлежащей_классу_функции

Итак, открытие и присоединение файла к конкретному файловому потоку обеспечивается таким вызовом функции open():

имя_потока.open(имя_файла, режим, защита);

Здесь имя_потока - имя одного из объектов, принадлежащих классам ofstream, ifstream, fstream. Примеры вызовов для определенных выше потоков:

outFile.open("С:\\USER\\RESULT.DAT");

inFile.open("DATA.TXT");

ioFile.open("CHANGE.DAT",ios::out);

При открытии файлов с потоками класса ofstream второй параметр по умолчанию устанавливается равным ios::out, т.е. файл открывается только для вывода. Таким образом, файл C:\USER\RESULT.DAT после удачного выполнения функции open() будет при необходимости (если он не существовал ранее) создан, а затем открыт для вывода (записи) данных в текстовом режиме обмена и присоединен к потоку outFile. Теперь к потоку outFile может применяться, например, операция включения <<, как к стандартным выходным потокам cout, cerr.

Поток inFile класса ifstream в нашем примере присоединяется функцией open() к файлу с именем DATA.TXT. Этот файл открывается для чтения из него данных в текстовом режиме. Если файла с именем DATA.TXT не существует, то попытка вызвать функцию inFile.open() приведет к ошибке.

Для проверки удачности завершения функции open() используется перегруженная операция !. Если унарная операция ! применяется к потоку, то результат ненулевой при наличии ошибок. Если ошибок не было, то выражение !имя_потока имеет нулевое значение. Таким образом, можно проверить результат выполнения функции open():

. . . .