Пусть имеется новый проект консольного приложения под названием
FileStreamApp
(и в файле кода C# импортировано пространство имен
System.IO
и
System.Text
). Целью будет запись простого текстового сообщения в новый файл по имени
myMessage.dat
. Однако с учетом того, что
FileStream
может оперировать только с низкоуровневыми байтами, объект типа
System.String
придется закодировать в соответствующий байтовый массив. К счастью, в пространстве имен
System.Text
определен тип
Encoding
, предоставляющий члены, которые кодируют и декодируют строки в массивы байтов.
После кодирования байтовый массив сохраняется в файле с помощью метода
FileStream.Write()
. Чтобы прочитать байты обратно в память, понадобится сбросить внутреннюю позицию потока (посредством свойства
Position
) и вызвать метод
ReadByte()
. Наконец, на консоль выводится содержимое низкоуровневого байтового массива и декодированная строка. Ниже приведен полный код:
using System;
using System.IO;
using System.Text;
<b>// He забудьте импортировать пространства имен System.Text и System.IO.</b>
Console.WriteLine("***** Fun with FileStreams *****\n");
<b>// Получить объект FileStream.</b>
using(FileStream fStream = File.Open("myMessage.dat",
FileMode.Create))
{
<b> // Закодировать строку в виде массива байтов.</b>
string msg = "Hello!";
byte[] msgAsByteArray = Encoding.Default.GetBytes(msg);
<b> // Записать byte[] в файл.</b>
fStream.Write(msgAsByteArray, 0, msgAsByteArray.Length);
<b> // Сбросить внутреннюю позицию потока.</b>
fStream.Position = 0;
<b> // Прочитать byte[] из файла и вывести на консоль.</b>
Console.Write("Your message as an array of bytes: ");
byte[] bytesFromFile = new byte[msgAsByteArray.Length];
for (int i = 0; i < msgAsByteArray.Length; i++)
{
bytesFromFile[i] = (byte)fStream.ReadByte();
Console.Write(bytesFromFile[i]);
}
<b> // Вывести декодированное сообщение.</b>
Console.Write("\nDecoded Message: ");
Console.WriteLine(Encoding.Default.GetString(bytesFromFile));
Console.ReadLine();
}
File.Delete("myMessage.dat");
В приведенном примере не только производится наполнение файла данными, но также демонстрируется основной недостаток прямой работы с типом
FileStream
: необходимость оперирования низкоуровневыми байтами. Другие производные от
Stream
типы работают в похожей манере. Например, чтобы записать последовательность байтов в область памяти, понадобится создать объект
MemoryStream
.
Как упоминалось ранее, в пространстве имен
System.IO
доступно несколько типов для средств чтения и записи, которые инкапсулируют детали работы с типами, производными от
Stream
.
Работа с типами StreamWriter и StreamReader
Классы
StreamWriter
и
StreamReader
удобны всякий раз, когда нужно читать или записывать символьные данные (например, строки). Оба типа по умолчанию работают с символами Unicode; тем не менее, это можно изменить за счет предоставления должным образом сконфигурированной ссылки на объект
System.Text.Encoding
. Чтобы не усложнять пример, предположим, что стандартная кодировка Unicode вполне устраивает.
Класс
StreamReader
является производным от абстрактного класса по имени
TextReader
, как и связанный с ним тип
StringReader
(обсуждается далее в главе). Базовый класс
TextReader
предоставляет каждому из своих наследников ограниченный набор функциональных средств, в частности возможность читать и "заглядывать" в символьный поток.
Класс
StreamWriter
(а также
StringWriter
, который будет рассматриваться позже) порожден от абстрактного базового класса по имени
TextWriter
, в котором определены члены, позволяющие производным типам записывать текстовые данные в текущий символьный поток.
Чтобы содействовать пониманию основных возможностей записи в классах
StreamWriter
и
StringWriter
, в табл. 20.8 перечислены основные члены абстрактного базового класса
TextWriter
.
На заметку! Вероятно, последние два члена класса
TextWriter
покажутся знакомыми. Вспомните, что тип
System.Console
имеет члены
Write()
и
WriteLine()
, которые выталкивают текстовые данные на стандартное устройство вывода. В действительности свойство
Console.In
является оболочкой для объекта
TextWriter
, a
Console.Out
— для
TextWriter
.
Производный класс
StreamWriter
предоставляет подходящую реализацию методов
Write()
,
Close()
и
Flush()
, а также определяет дополнительное свойство
AutoFlush
. Установка этого свойства в
true
заставляет
StreamWriter
выталкивать данные при каждой операции записи. Имейте в виду, что за счет установки
AutoFlush
в
false
можно достичь более высокой производительности, но по завершении работы с объектом
StreamWriter
должен быть вызван метод
Close()
.