Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции OOP c#.doc
Скачиваний:
44
Добавлен:
22.09.2019
Размер:
3.38 Mб
Скачать

2.5. Использование потоков данных

Для поддержки операций, связанных с вводом и выводом информации, библиотека классов платформы .NET предоставляет пространство имен System.IO. Основное понятие, связанное с элементами данного пространства имен, – это поток. Поток – абстрактное представление данных в виде последовательности байт. Потоки (в отличие от файлов) могут быть ассоциированы с файлами на диске, памятью, сетью. В пространстве имен System.IO поток представлен абстрактным классом Stream. От данного абстрактного класса порождены классы System.IO.FileStream (работа с файлами как с потоками), System.IO.MemoryStream (поток в памяти), System.Net.Sockets.NetworkStream (работа с сокетами как с потоками), System.Security.Cryptography.CryptoStream (потоки зашифрованных данных).

Рассмотрим основные методы и свойства класса Stream. Свойства для чтения CanRead, CanWrite и CanSeek определяют, поддерживает ли поток чтение, запись и поиск. Если поток поддерживает поиск, перемещаться по потоку можно при помощи метода Seek(). На текущую позицию в потоке указывает свойство Position (нумерация с нуля). Свойство Length возвращает длину потока, которая может быть установлена при помощи метода SetLength(). Методы Read() и ReadByte(), Write() и WriteByte() служат для чтения и записи блока байт или одиночного байта. Метод Flush() записывает данные из буфера в связанный с потоком источник данных. При помощи метода Close() поток закрывается и все связанные с ним ресурсы освобождаются.

Класс Stream вводит поддержку асинхронного ввода/вывода. Для этого служат методы BeginRead() и BeginWrite(). Уведомление о завершении асинхронной операции возможно двумя способами: или при помощи делегата тип System.AsyncCallback, передаваемого как параметр методов BeginRead() и BeginWrite(), или при помощи вызова методов EndRead() и EndWrite(), которые приостанавливают текущий поток управления до окончания асинхронной операции.

Использование методов и свойств класса Stream продемонстрируем в фрагменте кода с классом FileStream. Объект класса FileStream возвращается некоторыми методами классов FileInfo и File, кроме этого, данный объект можно создать при помощи конструктора с параметрами, включающими имя файла и режимы доступа к файлу.

// Создаем файл test.dat в текущем каталоге

FileStream fs = new FileStream("test.dat",

FileMode.OpenOrCreate,

FileAccess.ReadWrite);

// В цикле записываем туда 100 байт

for (byte i = 0; i < 100; i++) {

fs.WriteByte(i);

}

// Мы можем записывать информацию из массива байт

byte[] info = {1, 2, 3, 4, 5, 6, 7, 8, 9};

// Первый параметр – массив, второй – смещение в массиве,

// третий – количество записываемых байт

fs.Write(info, 2, 4);

// Возвращаемся на начало файла

fs.Position = 0;

// Читаем все байты и выводим на экран

while (fs.Position <= fs.Length - 1) {

Console.Write(fs.ReadByte());

}

// Закрываем поток (и файл), освобождая ресурсы

fs.Close();

Класс MemoryStream предоставляет возможность организовать поток в оперативной памяти. Свойство Capacity этого класса позволяет получить или установить количество байтов, выделенных под поток. Метод ToArray() записывает все содержимое потока в массив байт. Метод WriteTo() переносит содержимое потока в памяти в другой поток, производный от класса Stream.

Классы-потоки представляют поток как последовательность неформатированных байт. Однако в большинстве приложений удобнее читать и записывать в поток данные примитивных типов или строк. Библиотека классов .NET Framework содержит набор парных классов вида XXXReader/XXXWriter, которые инакапсулируют поток и предоставляют к нему высокоуровневый доступ.

Классы BinaryReader и BinaryWriter позволяют при помощи своих методов читать и записывать в поток данные примитивных типов, строк и массивов байт или символов. Вся информация записывается в поток как последовательность байт. Рассмотрим работу с данными классами на следующем примере. Пусть имеется класс, который хранит информацию о студенте:

class Student {

public string Name;

public int Age;

public double MeanScore;

}

Методы, которые осуществляют запись и чтение объекта этого класса в поток в виде последовательности байт, могут иметь следующий вид:

void SaveToStream(Stream stm, Student s) {

// Конструктор класса позволяет "обернуть"

// BinaryWriter вокруг потока

BinaryWriter bw = new BinaryWriter(stm);

// BinaryWriter содержит 18 перегруженных версий

// метода Write()

bw.Write(s.Name);

bw.Write(s.Age);

bw.Write(s.MeanScore);

// Убеждаемся, что буфер BinaryWriter пуст

bw.Flush();

}

void ReadFromStream(Stream stm, Student s) {

BinaryReader br = new BinaryReader(stm);

// Для чтения каждого примитивного типа есть свой метод

s.Name = br.ReadString();

s.Age = br.ReadInt32();

s.MeanScore = br.ReadDouble();

}

Абстрактные классы TextReader и TextWriter позволяю читать и записывать данные в поток как последовательность символов. От этих классов наследуются классы StreamReader и StreamWriter. Перепишем методы для сохранения данных класса Student с использованием StreamReader и StreamWriter:

void SaveToStream(Stream stm, Student s) {

StreamWriter sw = new StreamWriter(stm);

// Запись напоминает вывод на консоль

sw.WriteLine(s.Name);

sw.WriteLine(s.Age);

sw.WriteLine(s.MeanScore);

sw.Flush();

}

void ReadFromStream(Stream stm, Student s) {

StreamReader sr = new StreamReader(stm);

// Читаем данные как строки, требуется их преобразовать

s.Name = sr.ReadLine();

s.Age = Int32.Parse(sr.ReadLine());

s.MeanScore = Double.Parse(sr.ReadLine());

}

Классы StringReader и StringWriter – это наследники классов TextReader и TextWriter, которые представляют доступ к строке или к объекту класса StringBuilder как к потоку. Это может оказаться полезным, если текстовая информация добавляется в специальный буфер в оперативной информации. Работу с данными классами иллюстрирует следующий пример:

StringWriter sw = new StringWriter();

// Пишем информацию в поток

sw.WriteLine("Hello!");

sw.WriteLine("This is an example...");

sw.Close();

// Выводим все информацию

Console.WriteLine(sw.ToString());

string s = "Big\n Big string\n 10";

// Создаем StringReader на основе строки

StringReader sr = new StringReader(s);

// Последовательно читаем "кусочки" строки

string input;

while((input = sr.ReadLine()) != null)

Console.WriteLine(input);

sr.Close();