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

4 Создание потоков, связанных с файлами

Если вам нужно создать входной или выходной поток, связанный с локальным файлом, содержащим двоичные данные, следует воспользоваться классами BinaryWriter и BinaryReader из библиотеки Microsoft .NET Framework. Что же касается чтения из файла или записи в файл текстовых данных, то для решения этой задачи обычно используются классы StreamReader и StreamWriter.

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

Обращаем ваше внимание на то, что система сборки мусора, встроенная в среду исполнения Microsoft .NET Framework, следит за использованием только объектов, находящихся в оперативной памяти, таких, как переменные, массивы, экземпляры объектов, созданных на базе классов и т. п. Что же касается потоков, связанных с файлами, то вы должны сами следить за их открытием и закрытием.

4.1 Открытие потока FileStream

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

FileStream fs = new FileStream("myfile.dat", FileMode.CreateNew);

В качестве первого параметра конструктору необходимо передать полный путь к файлу или имя файла, а в качестве второго — режим открытия потока (табл. 1).

Режим

Append

Create

CreateNew

Open

OpenOrCreate

Truncate

Таблица 1: Режимы открытия файла FileMode

Описание

Если файл существует, он открывается. Текущая позиция устанавливается на конец файла. Если указанного файла нет, то он создается. Этот режим можно использовать только совместно с режимом доступа FileAccess .Write. При попытке чтения из файла, открытого подобным образом, возникает исключение ArgumentException

ОС должна создать новый файл. Если указанный файл уже существует, он будет перезаписан

ОС должна создать новый файл. Если указанный файл уже существует, возникнет исключение IOException

Требуется открыть существующий файл. Необходим доступ FilelOPermissionAccess . Read. Если требуемый файл не найден, возникает исключение System. 10. FileNotFoundException

Если указанный файл существует, он должен быть открыт. В противном случае ОС должна создать и открыть указанный файл

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

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

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

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

FileStream fs = new FileStream(name, FileMode.Open, FileAccess.Read);

FileStream fsi = new FileStream(name, FileMode.Open, FileAccess,Read, FileShare.Read);

Остановимся на режиме доступа к файлу, передаваемом приведенным выше конструкторам через третий параметр (табл. 2). Режим доступа задастся как статическая константа класса FileAccess.

 

Таблица 2: Режимы доступа FileAccess

 

 

Режим

Тип доступа

 

 

Read

Доступ только на чтение

ReadWrite

Доступ на чтение и запись

Write

Доступ на запись

При необходимости программа может задать режим совместного использования файла, когда для одного и того же файла одновременно создается несколько потоков. Этот режим задается при помощи статических констант класса FileShare (табл. 3).

Таблица 3: Режимы совместного использования файла FileShare

Режим

Тип доступа

Inheritable

Идентификатор файла может наследоваться дочерним процессом

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

будет закрыт

Read

Допускаются дополнительные запросы на открытие файла для чтения

ReadWrite

Допускаются дополнительные запросы на чтение и запись

Write

Допускаются дополнительные запросы на запись

При необходимости программа может задать режим совместного использования файла, когда для одного и того же файла одновременно создается несколько потоков. Этот режим задается при помощи статических констант класса FileShare (табл. 3).

4.2 Открытие потоков BinaryWriter и BinaryReader

Итак, ваша программа создала поток на базе файла, воспользовавшись для этого классом FileStream. Далее на базе полученного таким образом потока необходимо создать потоки классов BinaryWriter и BinaryReader, предназначенные соответственно для записи в файл и чтения из файла двоичных данных:

BinaryWriter bw = new BinaryWriter(fs);

BinaryReader br = new BinaryReader(fs);

Работа с потоками BinaryWriter и BinaryReader будет описана чуть позже.

4.3 Закрытие потоков

После того как программа завершила работу с потоками, она должна их закрыть.

Для закрытия потоков используйте метод Close. Закрывайте потоки в порядке, обратном открытию. Если вы вначале открыли поток FileStream, а затем создали на его базе потоки класса BinaryWriter и BinaryReader, то закрывать потоки нужно в обратном порядке: вначале закройте потоки BinaryWriter и BinaryReader, а затем поток FileStream:

FileStream fs = new FileStream("myfile.dat", FileMode,CreateNew); BinaryWriter bw = new BinaryWriter(fs);

BinaryReader br = new BinaryReader(fs); //работа с потоками

bw.Close(); br.Close(); fs.Close();

4.4 Запись двоичных данных

Для записи двоичных данных в поток BinaryWriter предусмотрено несколько перегруженных методов Write, приведенных ниже:

public virtual void Write(bool); public virtual void Write(byte); public virtual void Write(byte[]); public virtual void Write(char); public virtual void Write(char[]); public virtual void Write(decimal); public virtual void Write(double); public virtual void Write(short); public virtual void Write(int); public virtual void Write(long); public virtual void Write(sbyte); public virtual void Write(float); public virtual void Write(uint); public virtual void Write(ulong); public virtual void Write(string);

public virtual void Write(byte[], int, int); public virtual void Write(char[], int, int);

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

При записи в двоичный поток текстовой строки string она будет предваряться префиксом, содержащим значение длины строки. Строка будет записана в кодировке ASCII.

Последние два перегруженных метода Write позволяют записать в поток массив

соответственно двоичных байтов и символов UNICODE. Второй параметр метода задает начальный индекс блока данных в массиве, а третий — размер блока (соответственно в байтах и символах).

4.5 Чтение двоичных данных

Чтобы прочитать данные из потока BinaryReader, нужно использовать один из методов, специально предусмотренных для этого в классе BinaryReader. Мы привели краткий список этих методов в табл. 4.

 

Таблица 4: Методы для чтения данных класса BinaryReader

 

 

Метод

Тип данных, считываемых из потока

 

 

 

 

ReadBoolean

Один объект типа Boolean

ReadByte

I байт данных

ReadBytes

Чтение заданного количества байтов данных

ReadChar

Один текстовый символ

ReadChars

Чтение заданного количества текстовых символов

ReadDecimal

Десятичное значение размером 16 байт

ReadSingle

Значение с плавающей десятичной точкой размером 4 байта

ReadDouble

Значение с плавающей десятичной точкой размером 8 байт

ReadsByte

Число со знаком размером 1 байт

ReadlntlG

Число со знаком размером 2 байта

Readlnt32

Число со знаком размером 4 байта

Readlnt64

Число со знаком размером 8 байт

ReadUIntl6

Число без знака размером 2 байта

ReadUInt32

Число без знака размером 4 байта

ReadUInt64

Число без знака размером 8 байт

ReadString

Текстовая строка с префиксом длины

4.6 Программа Binary

Программа Binary (листинг 2) демонстрирует некоторые приемы работы с двоичными файлами при помощи классов FileStream, BinaryWriter и BinaryReader.

Листинг 2.

using System; using System.10;

namespace Binary

{

class BinaryApp

{

private const string testFile = "mydata.dat";

static void Main(string[] args}

{

if(File,Exists(testFile))

{

Console.WriteLine("Файл {0} уже существует", testFile); Console.ReadLine();

return;

}

FileStream fs = new FileStream(testFile, FileMode.CreateNew); BinaryWriter bw = new BinaryWriter(fs); bw.Write("Text string");

for(uint i = 0; i < 20; i++)

{

bw.Write((uint)i);

}

bw.Closef); fs.Close();

fs = new FileStream(testFile, FileMode.Open, FileAccess.Read); BinaryReader br = new BinaryReader(fs);

string s = br.ReadString(); Console.WriteLine[s);

for (uint i = 0; i < 20; i+ + )

{

Console.Write("(0} ", br.ReadUInt32());

}

br.Close(); fs.Close(};

}

}

В самом начале своей работы программа проверяет существование файла с именем mydata.dat, используя для этого метод Exists класса File:

if (File.Exists[testFile})

{

Console.WriteLine("Файл (0} уже существует", testFile); Console.ReadLine();

return;

}

Класс File, предназначенный для работы с файлами и каталогами, мы рассмотрим позже. Пока же мы только заметим, что данный фрагмент кода проверяет существование файла mydata.dat в каталоге, который был текущим при запуске нашей программы на выполнение. Если такой файл там уже существует (например, был создан при предыдущем запуске этой программы), программа завершает свою работу с сообщением об ошибке.

Если же в текущем каталоге нет файла с именем mydata.dat, программа создает его, вызывая для этого конструктор класса FileStream с соответствующими параметрами:

FileStream fs = new FileStreamftestFile, FileMode.CreateNew);

Так как мы указали режим создания потока FileMode.CreateNew, то новый файл будет создан в том случае, если файла с таким именем нет в текущем каталоге.

После создания потока FileStream, связанного с файлом, мы создаем поток bw класса BinaryWriter, предназначенный для записи в файл двоичных данных:

BinaryWriter bw = new BinaryWriter(fs);

На следующем шаге программа записывает в поток текстовую строку:

bw.Write("Text string");

Далее программа записывает в поток bw 20 чисел типа uint:

for(uint i = 0; i < 20; i++)

{

bw.Write((uint)i);

}

После завершения записи оба потока закрываются методом Close:

bw.Close(); fs.Close();

На следующем этапе наша программа считывает содержимое только что записанного файла и отображает его на консоли.

Для этого она вначале открывает ноток fs класса FileStream, указывая, что файл должен быть открыт только для чтения:

fs = new FileStreamftestFile, FileMode.Open, FileAccess.Read);

Режим чтения задается константой FileAccess .Read.

Далее на базе потока f s программа создает поток Ьг класса BinaryReader:

BinaryReader br = new BinaryReader(fs);

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

string s = br.ReadString(); Console.WriteLine(s);

Далее она в цикле считывает 20 чисел и также отображает их на консоли:

for (uint i = 0; i < 20; i++)

{

Console.Write("{0}”, br.ReadUInt32());

}

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

br.Close(); fs.Close();

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

Console.WriteLine("Файл успешно создан"); Console.ReadLine();