
- •1 Файлы и потоки
- •2 Потоки данных и классы
- •2.1 Стандартные потоки
- •2.2 Базовые классы для работы с файлами и потоками
- •2.2.1 Основные классы ввода и вывода
- •2.2.2 Классы на базе FileSystemInfo
- •2.2.3 Классы для работы с потоками
- •2.2.4 Классы для работы с потоками текстовых символов
- •2.3 Перечисления
- •3 Работа со стандартными потоками
- •3.1 Стандартный поток ввода
- •3.2 Стандартный поток вывода
- •3.2.1 Стандартный поток вывода сообщений об ошибках
- •3.3 Программа StdStreams
- •4 Создание потоков, связанных с файлами
- •4.1 Открытие потока FileStream
- •4.2 Открытие потоков BinaryWriter и BinaryReader
- •4.3 Закрытие потоков
- •4.4 Запись двоичных данных
- •4.5 Чтение двоичных данных
- •4.6 Программа Binary
- •4.7 Работа с текстовыми файлами
- •5 Выбор кодировки символов
- •5.1 Кодовые страницы
- •5.2 Недостатки модели кодовых страниц
- •5.3 Стандарт UNICODE
- •5.3.1 Unicode в Microsoft Windows NT/2000/XP
- •5.3.2 UNICODE в Microsoft Windows 95
- •5.4 Кодировка текстовых потоков
- •5.5 Кодировка текстовых строк в двоичных потоках
- •6 Буферизация потоков
- •6.1 Буферизация двоичных потоков
- •6.2 Буферизация текстовых потоков
- •6.3 Принудительный сброс буферов
- •7 Потоки в оперативной памяти
- •7.1 Создание потока
- •7.2 Чтение данных
- •7.3 Запись данных
- •7.4 Доступ к буферу потока MemoryStream
- •7.5 Потоки на базе строк string
- •7.6 Потоки класса StringBuilder
- •8 Краткие выводы
- •8.1 Создание и чтение бинарного файла
- •8.2 Создание текстового файла
- •8.3 Добавление в текстовый файл
- •8.4 Чтение и запись в файл строк на русском языке
- •8.5 Посимвольное чтение текстового файла
- •8.6 Построчное чтение текстового файла
- •8.7 Чтение файла полностью

значение -1, если был достигнут конец потока.
После отображения всех данных, хранящихся в потоке, мы закрываем поток методом Close:
ms.Close();
7.4 Доступ к буферу потока MemoryStream
Как вы уже знаете, поток MemoryStream может быть сформирован на базе предварительно проинициализированного массива байтов или путем последовательной записи в него данных методами Write и WriteByte. При этом данные фактически будуг храниться во внутреннем буфере потока.
С помощью метода GetBuf f ег вы можете получить доступ к буферу потока. Этот метод возвращает ссылку на массив байтов, представляющий собой текущее содержимое буфера.
Метод ТоАггау позволяет преобразовать поток (даже закрытый методом Close) в массив байтов.
Программа, исходный текст которой представлен в листинге 10, демонстрирует использование упомянутых выше методов.
Листинг 10.
using System; using System.IO;
namespace GetBuffer
{
class GetBufferApp
{
static void Main(string[] args)
{
MemoryStream ms = new MemoryStream();
for (byte i = 0; i < 10; i++)
{
ms.WriteByte(i);
}
ms.Flush();
byte[] buf = ms.GetBuffer();
foreach (byte bt in buf) Console,Write(bt);
Console.WriteLine(); ms.Close ();
byte[] buf1 = ms.ToArray();
foreach (byte bt in bufl) Console.Write(bt);
Console.ReadLine();
}
}
}
Получив управление, программа создаст поток в памяти и записывает в него 10 целых чисел. После этого происходит сброс буфера:
MemoryStream ms = new MemoryStream();
for (byte i = 0; 1 < 10; i + + )
{
ms.WriteByte(i);
}
ms.Flush ();
Далее наша программа получает доступ к буферу открытого потока, вызывая метод

GetBuffer:
byte[] buf = ms.GetBuffer ();
В результате в массив buf будет записано содержимое текущего буфера потока. Программа отображает его на консоли с помощью простого цикла:
foreach (byte bt in buf) Console.Write(bt);
Заметим, что, хотя мы записали в поток всего 10 байт, на консоль будут выведены не только записанные числа, но и дополнительные нули. Это получается потому, что по умолчанию размер буфера превышает 10 байт. Метод GetBuffer возвращает нам полное содержимое буфера потока, изначально проинициализированного нулевыми значениями.
После вывода содержимого буфера потока на консоль наша программа закрывает поток:
ms.Close ();
Несмотря на то что поток закрыт, хранящиеся в нем данные все еще могут быть доступны. С помощью метода ТоАггау программа может преобразовать закрытый поток в массив байтов, как это показано ниже:
byte[] bufl = ms.ТоАггау();
Далее содержимое полученного таким способом массива отображается на консоли:
foreach (byte bt in bufl) Console.Write(bt);
Обратите внимание, что во второй раз будет выведено только 10 чисел:
012345678900000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000
0123456789
Дело в том, что метод ТоАггау преобразует только те данные, которые были записаны в поток, а не весь буфер потока.
7.5 Потоки на базе строк string
Рассказывая о потоках в оперативной памяти, следует упомянуть и потоки, создаваемые на базе текстовых строк класса string.
Чтобы создать входной поток на базе строки string, вам потребуется класс StringReader.
Создав такой поток с помощью конструктора класса StringReader, программа сможет читать из него отдельные символы или блоки символов с помощью перегруженных методов Read.
Использование этих методов демонстрируется в программе, исходный текст которой представлен в листинге 14.11.
Листинг 11.
using System; using System.IO;
namespace StringReaderDemo
{
class StringReaderDemoApp
{
static void Main(string[] args)
{
string str = "Каждый охотник желает знать, где сидит фазан"; StringReader sr = new StringReader(str);
int ch;
while(true)
{
ch = sr.Read();
if(ch == -1) break; Console.Write((char)ch);
}
Console.WriteLine(); sr.Close();
sr = new StringReader(str); char[] b = new char[str.Length];
sr.Read(b, 0, 15); Console.WriteLine(b); sr.Close(); Console.ReadLine();
}
}
)
Вэтой программе мы определили текстовую строку str класса string, а затем создали на
ееоснове поток sr класса StringReader:
string str = "Каждый охотник желает знать, где сидит фазан"; StringReader sr = new StringReader(str);
Далее мы в цикле считываем символы из потока и отображаем их на консоли:
int ch;
while(true)
{
ch = sr.Read();
if (ch == -1) break; Console.Write((char)ch);
}
Заметим, что метод Read возвращает значение int, а не char. Поэтому перед отображением прочитанного символа на консоли мы выполняем необходимое явное преобразование типов.
Завершив работу с потоком, мы закрываем его обычным образом с помощью метода Close:
sr.Close();
Далее в программе демонстрируется чтение блока символов. Для этого мы снова открываем поток:
sr = new StringReader(str);
Используя перегруженный метод Read, мы читаем первые 15 символов из потока в

массив data:
char[] data = new char[str.Length]; sr.Read(data, 0, 15);
Далее программа отображает на консоли содержимое массива и закрывает поток:
Console.WriteLine(data);
sr.Close();
Вот что появится на консоли, после того как программа завершит свою работу:
Каждый охотник желает знать, где сидит фазан Каждый охотник
7.6 Потоки класса StringBuilder
С помощью класса StringBuilder вы можете создавать потоки на базе текстовых строк, которые затем поддаются изменению. Например, создав такой поток, программа может вставлять в произвольное место потока новые текстовые строки или добавлять их в конец потока, а также удалять и замещать фрагменты строк.
Основные приемы работы с потоками класса StringBuilder демонстрируются в программе, исходный текст которой представлен в листинге 12.
Листинг 12.
using System; using System.IO; using System.Text;
namespace StringBuilderDemo
{
class StringBuilderDemoApp
{
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder("охотник желает");
sb.lnsert(0, "Каждый "); sb.Append(" знать, где сидит"};
StringWriter sw = new StringWriter(sb);
char[) data = {'ф' , 'a', 'з', 'a', 'н' , '.'}; sw.Write(data);
Console.WriteLine(sb);
sw.Close(); Console.ReadLine();
}
}
}
Прежде всего наша программа создаст поток StringBuilder с помощью конструктора и записывает в него фрагмент фразы про охотника:
StringBuilder sb = new StringBuilder("охотник желает");
Далее, пользуясь методами Insert и Append, программа вставляет один фрагмент фразы в начало потока, а другой — в конец потока:
sb.lnsert(0, "Каждый "); sb.Append(" знать, где сидит"};
При необходимости вы можете воспользоваться методами Remove и Replace соответственно для удаления из потока фрагмента строки и для замены его другим фрагментом.
Методу Remove в качестве первого параметра нужно передать начальный индекс удаляемого фрагмента, а в качестве второго — длину удаляемого фрагмента в байтах:
public Remove(int index, int size);
Что же касается метода Replace, то определено его несколько перегруженных вариантов.
Следующие два метода заменяют в потоке соответственно все заданные символы и строки другими:
public Replace(char chl, char ch2); public Replace(string strl, string str2);
Эти методы делают замену во всем потоке.
Следующие два метода позволяют ограничить область замены блоком, начало которого задано параметром index, а размер — параметром size:
public Replace(char chl, char ch2, int index, int size); public Replace(string strl, string str2, int index, int size);
Вернемся, однако, к описанию нашей программы.
На следующем шаге программа создает поток StringWriter на базе потока StringBuilder:
StringWriter sw = new StringWriter(sb);
Пользуясь этим потоком с помощью метода Write, паша программа дописывает в конец фразы последнее слово:
char[) data = {'ф' , 'a', 'з', 'a', 'н' , '.'}; sw.Write(data);
Далее содержимое потока выводится на консоль, после чего поток закрывается методом Close:
Console.WriteLine(sb); sw.Close();
В классе StringWriter определено множество перегруженных методов Write, позволяющих записывать в поток данные практически всех встроенных типов. Кроме того, предусмотрены аналогичные методы WriteLine, добавляющие после записи символ перевода строки.