Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
файлы лекция (основная).pdf
Скачиваний:
31
Добавлен:
15.04.2015
Размер:
938.85 Кб
Скачать

6.3 Принудительный сброс буферов

Еще один важный момент связан с буферизованными потоками. Как мы уже говорили, буферизация ускоряет работу приложений с потоками, так как при ее использовании сокращается количество обращений к системе ввода-вывода. Вы можете постепенно, в течение дня, добавлять в поток данные по 1 байту, и только к вечеру эти данные будут физически записаны в файл на диске.

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

Данный метод определен во всех классах, имеющих отношение к буферизованным потокам. Вот как он используется с буферизованными текстовыми потоками данных:

StreamWriter sw = new StreamWriter{testFile, false, System.Text.Encoding.UNICODE, 10000);

sw.WriteLine ("Каждый охотник желает знать, где сидит фазан!"); sw,WriteLine ("Число \"Пи\" равно примерно {0}.", 3.1415926);

sw.Flush(}; sw.Close{);

В этом фрагменте кода мы создали выходной буферизованный поток данных, записали в него две строки методом WriteLine, а затем сбросили буферы методом Flush. Закончив работу с потоком, мы его закрыли методом Close.

7 Потоки в оперативной памяти

ОС Microsoft Windows 95 и Microsoft Windows NT/2000/XP предоставляют на уровне своего программного интерфейса Win32 возможность работать с оперативной памятью как с файлом. Это очень удобно во многих случаях, в частности, файлы, отображаемые на память, можно использовать для передачи данных между одновременно работающими задачами и процессами. Подробнее об этом вы можете прочитать в 27-м томе «Библиотеки системного программиста», который называется «Операционная система Microsoft Windows NT для программиста. Часть 2» .

При создании программ С# вы также можете работать с объектами оперативной памяти как с файлами, а точнее говоря, как с потоками.

Ранее мы отмечали, что в библиотеке классов Microsoft .NET Framework есть класс MemoryStream, специально предназначенный для создания потоков в оперативной памяти. Рассмотрим основные приемы его использования.

7.1 Создание потока

Для создания потоков в оперативной памяти в классе MemoryStream предусмотрено несколько конструкторов.

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

MemoryStream ms = new MemoryStream();

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

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

MemoryStream ms = new MemoryStream(data);

Созданный таким способом поток будет содержать данные массива. Заметим, что размер такого потока не может быть увеличен (однако при необходимости его можно уменьшить методом SetLength).

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

MemoryStream ms = new MemoryStream(1000);

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

byte[] data = new byte []{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; MemoryStream msReadOnly = new MemoryStream(data, false);

В данном случае поток msReadOnly будет доступен только для чтения. При необходимости можно создать поток не из всего массива, а только из его части. Для этого нужно указать конструктору начальный индекс и размер блока данных, из которого будет образован массив:

byte[] data = new byte []{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; MemoryStream ms = new MemoryStream(data, 1, 5);

MemoryStream msReadOnly = new MemoryStream(data, 1, 5, false);

Здесь на базе части массива data мы создаем два потока— ms и msReadOnly. Первый из них доступен для чтения и записи, а второй — только для чтения.

7.2 Чтение данных

Если вы создали поток MemoryStream на базе проинициализированного массива байтов, то можете читать из него данные методами Read и ReadByte.

Первый из них читает блок данных, заданных смещением от начала потока и размером, возвращая прочитанные данные в виде массива. Текущая позиция продвигается вперед на количество байтов, равное размеру прочитанного блока. Второй метод (ReadByte) читает из потока 1 байт, продвигая текущую позицию вперед на 1 байт

Техника чтения данных из потока MemoryStream демонстрируется в программе, исходный текст которой приведен в листинге 8.

Листинг 8.

using System; using System.IO;

namespace MemoгуStreamDemо

{

class MemoryStreamDemoApp

{

static void Main(string[] args)

{

byte[] data = new byte [] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; byte[] dataCopy = new byte [10];

MemoryStream ms = new MemoryStream(data); ms.Read(dataCopy, 0, 9);

foreach (byte bt in dataCopy) Console.Write(bt);

ms.Close(); Console.ReadLine();

}

}

}

Массив data, на базе которого будет создаваться поток MemoryStream, проинициализирован целыми числами от 0 до 9, как это показано ниже:

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

Кроме того, в программе определен неинициализированный массив байтов dataCopy размером в 10 ячеек:

byte[] dataCopy = new byte [10];

Вначале наша программа создает поток ms на базе массива data:

MemoryStream ms = new MemoryStream(data);

Далее она читает содержимое всего потока в массив dataCopy:

ms.Read(dataCopy, 0, 9);

Вслед за этим содержимое массива dataCopy, проинициализированного содержимым потока, отображается на консоли:

foreach (byte bt in dataCopy) Console.Write(bt);

Завершив работу с потоком ms, программа закрывает его методом Close:

ms.Close ();

7.3 Запись данных

Для записи данных в поток MemoryStream вы можете использовать методы Write и WriteByte. Первый из этих методов записывает в поток 1 байт данных, а второй — массив байтов или часть этого массива.

В листинге 9 мы привели пример программы, которая сначала записывает данные в поток MemoryStream по байтам, а затем читает данные из этого потока и отображает их на консоли.

Листинг 9.

using System; using System.IO;

namespace MemoгуStreamDemo1

{

class MemoryStreamDemolApp

{

static void Main(string[] args)

{

MemoryStream ms = new MemoryStream();

for (byte i = 0; i < 10; i++)

{

ms.WriteByte(i);

}

ms.Flush();

ms.Seek(0, SeekOrigin.Begin);

while(true)

{

int data = ms.ReadByte(); if(data == -1) break;

Console.Write("{0} ", data);

}

ms.Close(); Console.ReadLine{);

}

}

}

Первым делом программа создает поток с помощью простейшего конструктора без параметров:

MemoryStream ms = new MemoryStreamО;

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

Далее наша программа в цикле записывает в поток 10 чисел, пользуясь для этого методом WriteByte:

for (byte i = 0; i < 10; i++)

{

ms.WriteByte(i);

}

Далее, для того чтобы из потока можно было читать, нам необходимо сбросить буфер методом Flash, а также переместить текущую позицию в начало потока:

ms.Flush();

ms.Seek(0, SeekOrigin.Begin);

Подробнее о позиционировании внутри потока мы расскажем позже, а сейчас заметим только, что методу Seek передаются два параметра. Первый параметр указывает смешение в байтах, а второй — место в потоке, от которого отсчитывается это смещение. Константа SeekOrigin.Begin задает смещение относительно начала потока.

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

Наша программа считывает их побайтно, вызывая для этого метод ReadByte:

while(true)

{

int data = ms.ReadByte(); if(data == -1) break;

Console.Write("{0} "r data);

}

Этот метод возвращает значение считанного байта, преобразованное в тип int, или