Назад | Начало урока | Вперед
Содержание

Глава 21

Вверх

Потоки данных

В этой главе:

В этой главе вы узнаете о чтении и записи данных в файлы и из файлов.
Как записывать и считывать данные разных типов.
Как осуществлять контроль формата исходящих данных.

Почти все программы занимаются тем, что принимают одни данные и возвращают
другие. Библиотеки классов среды .NET включают в себя набор потоковых классов,
предназначенных для организации процесса ввода-вывода. (Иногда ввод-вывод обозначается как I/O, что является сокращением от слов input/output.) Если вы пишете неуправляемую программу, к вашим услугам — библиотека функций iostream, которые также обслуживают процесс ввода-вывода данных.
Считывать и сохранять данные можно множеством способов. И библиотека iostream, и
.NET-классы ТО обеспечивают режим, при котором вам не нужно вникать во множество технических деталей, так как все они выполняются автоматически.

Вверх

.NET-классы I/O
В пространстве имен (namespace) System.IO среды .NET есть некоторое количество классов, предназначенных для организации процесса ввода-вывода информации в файлы и из файлов. В этой главе вы познакомитесь с классом StreamReader, который обеспечивает процесс чтения текста из файла, и с классом StreamWriter, который обеспечивает процесс записи текста в файл. Класс File используется как вспомогательный для создания объектов StreamReader и StreamWriter. Вот как открывается потоковый файл для записи текста:

StreamWriter *poWStream = File::CreateText( S "test.txt" ) ;

Запись в потоковый файл осуществляется посредством вызова метода Write:

poWStream->Write(S"Hello this is a test" ) ;

Метод Write является перегружаемым, благодаря чему вы можете записывать в файл
значения различных типов, например double или integer. Если нужно записать информацию, которая заканчивается символом окончания строки, вызовите метод WriteLine. К тому же не забывайте, что методы Write и WriteLine обладают возможностями форматирования, а потому вы можете набирать коды, наподобие этого:

pcWStream->Write(S"Hello {0}, you are {1} and {2} !", sName, nAge, sCute);

Записав в файл всю необходимую информацию, закройте поток с помощью метода

Close:poWStream->Close();

Подобным образом осуществляется чтение из файла посредством использования объекта класса StreamReader:

poRStream = File::OpenText(S"test.txt");
Console::WriteLine(poRStream->ReadLine());
poRStream~>Close();

Чтобы получить доступ к пространству имен System.IO, наберите в начале программы такой код:

using namespace System::IO;

Вверх

Записали — прочитали

В этом разделе рассматривается пример программы, в процессе выполнения которой вначале в файлы записываются текстовые и числовые данные, а затем те же данные считываются
из этих же файлов. Текстовые данные записываются в один файл, а числовые — в другой:

//Открытие потока для записи текста
StreamWriter poWStream = File::CreateText(S"text.txt");
//запись текста в файл
poWStream->WriteLine(S"Hello this is a test");
//Закрытие потока
poWStream->Close();

//Открытие другого файла для записи числовых данных
poWStream = File::CreateText(S"number.txt");
poWStream->WriteLine(15);
poWStream->WriteLine(42);
poWStream~>WriteLine(1);
poWStream->Close() ;

Далее используется объект класса StreamReader для открытия первого файла с последующим вызовом метода ReadLine для считывания текстовых данных:

//Открытие первого файла, считывание строк и отображение
//их на экране
StreamReader *poRStream = File::OpenText(S"text.txt");
String* pszWords = poRStream->ReadLine();
Console::WriteLine(pszWords);
poRStream~>Close() ;

Затем считываются данные из второго файла. Обратите внимание, что в файле содержится несколько значений. Метод ReadLine, достигнув конца файла, возвращает нулевое значение, что дает возможность использовать цикл while для того, чтобы прочитать из файла все данные. (О циклах while речь идет в главе ! 1.)

//Считывание числовых данных
String* pszLine;
int nTempNum;
poRStream = File::OpenText(S"numbers.txt") ;
//Считывание значений до тех пор, пока не будет достигнут
//конец файла
while ((pszLine = poRStream->ReadLine()) != 0)
{

nTempNum = Int32::Parse(pszLine);
Console::WriteLine(nTempNum);
poRStream->Close();
}

Функции библиотеки iostream

В библиотеке iostream содержится ряд функций, которые помогут записать данные в файл
и считать их оттуда. Файл для записи открывается таким несложным кодом:

ofstream foo("filename");

Специальный класс ofstream предназначен для передачи файлам выходных данных. (Буквы O и I взяты от слов output и files. Слово stream переводится как поток — термин, которым в C++ обозначаются объекты, предназначенные для организации процесса ввода-вывода данных.) В данном случае foo является потоковой переменной (объектом класса ofstream). При создании объекта foo конструктору класса ofstream передается имя файла, который должен быть открыт. Передача данных осуществляется с помощью оператора <<:

//Запись в файл текста "Hello World"
foo << "Hello World"

Класс ofstream имеет близнеца— класс ifstream, который предназначен для организации процесса чтения данных из файлов. (Здесь буквы i и/обозначают слова input и files.)
Использование класса if stream осуществляется аналогичным образом:

if stream foo("filename");
//Выделение области, куда будут считываться текстовые данные
char buffer[100];
//Считывание текстовых данных из файла
foo >> buffer;

Числовая информация считывается несколько иначе:

int Mylnt;
foo « Mylnt;

Да, и самое важное! Перед тем как использовать все эти возможности, нужно подключить
библиотеку iostream, для чего в начале программы наберите

#include <fstream.h>

Вверх

Повторное использование потоковых переменных

Когда потоковая переменная прекращает свое существование, представляемый ею файл закры-
вается. Но закрыть этот файл можно и раньше, воспользовавшись функцией-членом close:

//Закрытие файла, представляемого потоковой переменной foo
foo.close();

Обычно это делается в тех случаях, когда хотят одну и ту же потоковую переменную использовать для доступа к нескольким файлам. Чтобы сделать это, нужно вначале закрыть один файл, а затем с помощью функции-члена open открыть другой:

//Открытие файла "foo.txt"
foo.open("foo.txt");

Если хотите проверить, осталась ли еще в файле какая-то непрочитанная информация, используйте функцию-член eof, которая возвращает значение true (Истина) в момент достижения окончания файла.

Вверх

Записали — прочитали

Не волнуйтесь, это не дежа-вю. И даже не опечатка. Чуть ранее вы действительно видели раздел с точно таким же названием, но тогда описывался доступ к файлам с помощью .NET-классов I/O, а теперь рассматривается, как выполняются те же действия с использованием функций библиотеки iostream.
В этом разделе также рассматривается пример программы, в процессе выполнения которой вначале в файлы записываются текстовые и числовые данные, а затем эти же данные считываются. Текстовые данные записываются в один файл, а числовые — в другой:

//Открытие файла new.txt
ofstream oOutFile("new.txt");
//Запись Б этот файл текстовой информации
oOutFiie << "Row, row, row your boat";
//Теперь этот файл закрывается
oOutFile.close();
//Открытие другого файла и запись в него числовых данных.
//Обратите внимание, что числа отделяются друг от друга
//пробелами
oOutFile.open("number.txt") ;
oOutFile << 15 << " " « 42 << " " << 1;
oOutFile.close();

Далее для открытия файлов и считывания информации используется класс ifs~rea:n. В данном случае файл открывается не с помощью конструктора при создании потоковой переменной, а непосредственно, путем вызова метода open. He имеет значения, какой способ вы
выберете, оба работают одинаково хорошо.

ifstream oInFile;
оInFile.open ("new. txt." ) ;

Для сохранения информации, считываемой из файла, создается буфер. В данном случае
размер буфера равен пятидесяти байтам. Убедитесь, что размер используемого вами буфера
больше самого большого элемента, который должен быть считан. Если вы точно знаете, эле-
менты какого размера должны считываться, сделайте буфер больше, чем размер любого из
этих элементов. С другой стороны, установив размер буфера, вы можете ограничить макси-
мальный размер принимаемых программой данных.
Данные из файла считываются в буфер и затем по одному элементу за раз отображаются
на экране:

oInFile >> p;
cout << p << endl;
oInFile >> p;
cout << p << endl;

Затем данные считываются из файла, содержащего числовую информацию. Чтобы прочитав вес числа, используется цикл while. Функция eof на каждой итерации цикла проверяет, остаются ли еще данные для считывания. Поскольку символ окончания файла интерпретируется компилятором как число, чтобы он тоже не был отображен на экране как одно из
чисел, считываемых из файла, перед командой отображения данных на экране дополнительно
используется оператор if:

//Считывание данных из файла
while (!оInFile.eof())

//Чтение числа
olnFile » nTempNum;
//Если число не является символом окончания файла, оно
//отображается на экране
if (!oInFile.eof())
cout << nTempNum << endl;
}
oInFile.close();

Обратите внимание также на то, что информация из текстовых файлов считывается по
одному слову за раз. Это очень удобно для последующей обработки считываемых данных.

Вверх

Специальные команды, погружаемые в потоки данных

Ниже вы можете увидеть некоторые команды, которые включаются в поток для того, чтобы управлять способом чтения и записи данных. Сами по себе эти команды (называемые потоковыми манипуляторами) никуда не записываются и не считываются. Они лишь определяют особенности считывания или записи следующих за ними элементов:

Например, если вводимое пользователем число должно быть интерпретировано как зна-
чение типа hex, наберите такой код:

cin » hex >> TempNum;

Можно также использовать приведенный ниже код как вариант преобразования значения
типа hex к значению типа decimal:

//Чтение числа как значения типа hex
cin >> hex >> TempNum;
//Отображение этого же числа как значения типа decimal
cout << dec « TempNum;

Вверх

Формат передаваемых данных

По умолчанию числа выводятся на экран, на печать или записываются в файлы без добавления между ними каких-либо символов или пробелов. Если необходимо, их можно разделять
между собой дополнительными пробелами. Это особенно удобно, если вы хотите разместить
числа по столбцам. Чтобы отделить числа друг от друга пробелами, используйте функцию-
член width:

//Отображение чисел с интервалом в 20 пробелов
cout.width(20);

Вместо пробелов могут быть использованы какие-то другие символы. Например, можно
заполнить пространство между числами звездочками (*), чтобы никто не смог дописать, скажем, к платежному чеку парочку нулей:

//Заполнение звездочками пространства между отображаемыми числами
cout.fill('*');

Этот же прием можно использовать и при записи числовых данных в потоковые файлы.
Записывать данные в файлы, отображать на экране, а также считывать их оттуда можно
тысячами способов. Есть еще множество используемых для этого дополнительных классов, которые в книге не рассматриваются. Если вы хотите стать мастером ввода-вывода, обратитесь к разделу справочной системы Visual C++. который посвящен функциям ios-tream. Помимо чтения общих разделов о потоках данных и потоковых классах, можете обратить особое
внимание на разделы, посвященные методам форматирования, форматирующим флагам и
манипуляторам.

Вверх

Кое что о работе с файлами

Ниже приведено несколько советов, которые помогут вам при работе с файлами данных.


Назад | Начало урока | Вверх | Вперед
Содержание