C#. Иерархия потоков с опорными хранилищами. Класс FileStream.




Иерархия потоков с опорными хранилищами. Класс FileStream. Конструкторы класса FileStream. Создание экземпляров FileStream. Примеры. Обзор базовых методов


Содержание


Поиск на других ресурсах:

1. Иерархия потоков с опорными хранилищами. Рисунок

Все классы с опорными хранилищами унаследованы от класса Stream. Основными классами потоков с опорными хранилищами являются следующие:

  • FileStream — обеспечивает доступ к файлу. Класс поддерживает синхронную и асинхронную запись и чтение информации;
  • MemoryStream — реализует поток, резервным хранилищем которого является память;
  • IsolatedStorageFileStream — реализует доступ к файлу, который размещается в изолированном хранилище;
  • NetworkStream — обеспечивает основной поток данных для доступа к сети. Этот класс унаследован от класса FileStream;
  • PipeStream — обеспечивает объект типа Stream для основного канала, который поддерживает как анонимные, так и именованные каналы.

На рисунке 1 изображены классы, соответствующие потокам с опорными хранилищами. Классы сгруппированы по пространствам имен.

C#. Иерархия потоков с опорными хранилищами

Рисунок 1. Иерархия потоков с опорными хранилищами

Для того, чтобы использовать средства тому или иному классу нужно предварительно подключить соответствующее пространство имен. Например, чтобы использовать возможности класса NetworkStream в начале модуля программы нужно указать строку

using System.Net.Sockets;

 

2. Класс FileStream. Назначение. Создание экземпляра класса FileStream. Перечисления FileMode, FileAccess, FileShare, FileOptions

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

Класс FileStream поддерживает как синхронные, так и асинхронные операции записи в файл и чтения из файла.

Чтобы создать экземпляр класса FileStream нужно вызвать один из многочисленных конструкторов этого класса. Ниже приведен список некоторых из них.

public FileStream(string path, System.IO.FileMode mode)
public FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access)
public FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share)
public FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, int bufferSize)
public FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, int bufferSize, bool useAsync)
public FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, int bufferSize, System.IO.FileOptions options)

здесь

  • path – путь к файлу на диске или ином носителе информации;
  • mode — одно из значений перечисления FileMode, которое определяет режим доступа к файлу (Open, Append, Create);
  • access — задает доступ к файлу на основании множества констант перечисления FileAccess;
  • share — определяет вид управления доступом к файлу из других объектов на основании набора констант из перечисления FileShare;
  • bufferSize — положительное значение типа System.Int32, которое определяет размер буфера в байтах. По умолчанию bufferSize = 4096 байт;
  • useAsync — определяет, будет ли происходить синхронный или асинхронный ввод/вывод. Следует заметить, что не все операционные системы поддерживают асинхронный ввод/вывод. Если useAsync = true, то обеспечивается асинхронный ввод/вывод;
  • options — задает дополнительные опции настройки при создании объекта типа FileStream. Эти опции имеют фиксированные значения и представлены перечислением FileOptions.

В вышеприведенных конструкторах режим открытия файла mode задается из множества значений перечисления FileMode:

  • Append — режим открытия файла для добавления. Этот режим можно использовать в сочетании с режимом доступа FileAccess.Write. Если файл существует, то этот файл открывается и указатель чтения из файла перематывается на конец файла. Если файла не существует, то создается новый файл;
  • Create — режим создания нового файла. Этот режим используется в сочетании с доступом FileAccess.Write. Если файла не существует, то создается новый файл по указанному пути. Если файл существует, то он будет перезаписан;
  • CreateNew — режим, указывает операционной системе создать новый файл. Данный режим используется в сочетании с модификатором доступа FileAccess.Write. Отличие данного режима от FileMode.Create заключается в том, что если файл уже существует, то будет сгенерировано исключение System.IO.IOException;
  • Open — режим открытия уже существующего файла. Доступ к файлу (чтение, запись или чтение/запись) определяется значением из перечисления FileAccess (смотрите ниже). Если файл не существует, то будет сгенерировано исключение System.IO.FileNotFoundException;
  • OpenOrCreate — режим открытия файла. Если файла не существует, то создается новый файл. Файл может быть открыт с любым доступом (FileAccess.Read, FileAccess.Write, FileAccess.ReadWrite);
  • Truncate — задает режим открытия существующего файла с его усечением. Когда файл открыт, он должен быть усечен до нулевого размера. Этот режим сочетается с доступом к файлу FileAccess.Write. Если попытаться прочитать файл в режиме Truncate, будет сгенерировано исключение System.ArgumentException.

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

  • Read — файл доступен для чтения;
  • ReadWrite — файл доступен для записи и чтения;
  • Write — файл доступен для записи.

Перечисление FileShare определяет управление видом доступа других объектов типа FileStream и может иметь следующие значения:

  • Delete — дает разрешение на дальнейшее удаление файла;
  • Inheritable — указывает, что дочерние процессы могут наследовать обработчик файла;
  • None — запрещает доступ к текущему файлу. Любой запрос на открытие текущего файла будет закрыт;
  • Read — разрешает дальнейшее открытие файла для чтения. Если это значение не указано, то любая попытка открыть файл для чтения из другого процесса будет неудачной, пока файл не будет закрыт;
  • ReadWrite — дает разрешение на дальнейшее открытие файла для чтения или записи. Если этот флажок не указан, то любая попытка открыть файл с текущего или иного процесса будет неудачной;
  • Write — дает разрешение на дальнейшее открытие файла для записи. Если не указать это значение, то любая попытка открыть файл для записи из другого процесса (потока) будет неудачной до тех пор, пока файл не будет закрыт.

В перечислении FileOptions реализованы дополнительные возможности (опции), которые определяют назначение открытого файла:

  • Asynchronous — указывает, что файл может быть использован для асинхронного чтения и записи;
  • DeleteOnClose — указывает, что файл будет удален после того, как он больше не будет использоваться;
  • Encrypted — указывает, что файл зашифрован и его можно расшифровать только с помощью того же аккаунта пользователя, используемого для шифрования;
  • None — указывает, что никаких дополнительных опций не применяется для создаваемого объекта типа FileStream;
  • RandomAccess — указывает на то, что к файлу осуществляется случайный доступ. Это указание дает системе подсказку для оптимизации кэширования файлов;
  • SequentialScan — указывает на то, что файл читается последовательно от начала до конца. Это указание может быть использовано в качестве подсказки системе осуществить оптимизацию кэширования файлов. Это может дать некоторое повышение производительности;
  • WriteThrough — указание системе осуществлять запись через любой промежуточный кэш и переходить непосредственно на диск.

 

3. Примеры, демонстрирующие создание экземпляров типа FileStream для различных случаев применения
3.1. Создание файла. Простейший случай. Пример

Чтобы создать новый файл, нужно задать режим создания файла FileMode.Create. Особенность этого режима заключается в том, что если файл присутствует и он скрытый (hidden), то будет сгенерировано исключение UnauthorizedAccessException. Поэтому, для создания файла можно писать примерно следующий код:

// Создать новый файл с именем "myfile.txt".
// Если файл уже существует, то он будет перезаписан.
try
{
  FileStream fs = new FileStream("myfile.txt", FileMode.Create);

  // Использование файла
  // ...

  // Закрыть файл после использования
  fs.Close();
}
catch (UnauthorizedAccessException e)
{
  // Обработка, если файл присутствует и он есть скрытым (hidden)
  Console.WriteLine("Error: " + e.Message);
}

Создание файла не может быть с режимом доступа FileAccess.Read. Если написать такой код:

...

FileStream fs = new FileStream("myfile4.txt", FileMode.Create, FileAccess.Read);

...

то после запуска, программа сгенерирует исключение System.ArgumentException.

 

3.2. Открытие уже существующего файла для считывания. Простейший случай. Пример

Если файл уже есть на диске, то чтобы считать с него информацию без ее модификации, нужно использовать режим FileMode.Open и доступ FileAccess.Read. Если файла нет на диске, то будет сгенерировано исключение FileNotFoundException. Поэтому, нужно это исключение перехватить.

// Открытие файла для чтения
try
{
  FileStream fOpen = new FileStream("myfile5.txt", FileMode.Open, FileAccess.Read);

  // Чтение данных из файла
  // ...

  // После завершения закрыть файл
  fOpen.Close();
}
catch (FileNotFoundException e)
{
  // Обработка ошибки, если файл не найден
  Console.WriteLine("Error: " + e.Message);
}

 

3.3. Примеры обработки (запись/чтение) файла с указанным видом доступа. Перечисление FileShare

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

3.3.1. Чтение данных из файла разными (параллельными) объектами

Если нужно, чтобы обрабатываемый файл был считан другим объектом, то нужно указать FileShare.Read как показано далее

// Считывание из одного файла разными объектами FileStream
try
{
  // Разрешить считывание из файла "myfile.txt" данных другим объектом
  FileStream fs = new FileStream("myfile.txt", FileMode.Open, FileAccess.Read, FileShare.Read);

  // Создать другой объект, который будет считывать с того же файла
  FileStream fs2 = new FileStream("myfile.txt", FileMode.Open, FileAccess.Read);

  // Использование файла "myfile.txt" двумя объектами fs и fs2
  // ...

  // Закрыть файлы после использования
  fs.Close();
  fs2.Close();
}
catch (UnauthorizedAccessException e)
{
  // Обработка, если файл уже присутствует и он есть скрытым (hidden)
  Console.WriteLine("Error: " + e.Message);
}
catch (FileNotFoundException e)
{
  // Обработка, если файл не найден
  Console.WriteLine("Error: " + e.Message);
}

 

3.3.2. Запись данных в файл с различных объектов. Пример

Для того, чтобы одновременно записывать в файл с различных объектов, нужно при создании файлового объекта типа FileStream указать FileShare.Write по образцу ниже

...

// Запись в файл "myfile.txt" двумя разными объектами
try
{
  // Указать, что запись в файл "myfile.txt" будет несколькими объектами - FileShare.Write
  FileStream fw1 = new FileStream("myfile.txt", FileMode.Open, FileAccess.Write, FileShare.Write);

  // Создать другой объект, который будет записывать в тот же файл,
  // обязательно нужно указать FileShare.Write
  FileStream fw2 = new FileStream("myfile.txt", FileMode.Open, FileAccess.Write, FileShare.Write);

  // Использование файла "myfile.txt" двумя объектами - запись из двух разных экземпляров
  // ...

  // Закрыть файловые объекты после использования
  fw1.Close();
  fw2.Close();
}
catch (FileNotFoundException e)
{
  // Обработка, если файл не найден
  Console.WriteLine("Error: " + e.Message);
}

 

3.3.3. Пример записи двух чисел в файл и их считывание различными объектами

Данный пример демонстрирует:

  • запись в файл чисел типа double и int из двух разных экземпляров класса FileStream. При записи учитывается смещение;
  • чтение из файла чисел типа double и int из двух разных экземпляров класса FileStream. Как и в случае с записью, учитывается смещение;
  • возможности класса BitConverter для конвертирования различных типов в массив byte[] и наоборот;
  • использование и сочетание методов Write() и Seek() для записи данных различных типов;
  • использование и сочетание методов Read() и Seek() для чтения данных различных типов;
  • использование метода ReadByte() для формирования массива типа byte[].

 

using System;
using System.IO;
using System.Text;

namespace ConsoleApp10
{
  class Program
  {
    static void Main(string[] args)
    {
      // Запись в файл "myfile.txt" двумя разными объектами
      try
      {
        // Указать, что запись в файл "myfile.txt" будет несколькими объектами - FileShare.Write
        FileStream fw1 = new FileStream("myfile.txt", FileMode.Open, FileAccess.Write, FileShare.Write);

        // Создать другой объект, который будет записывать в этот же файл,
        // обязательно нужно указать FileShare.Write
        FileStream fw2 = new FileStream("myfile.txt", FileMode.Open, FileAccess.Write, FileShare.Write);

        // Использование файла "myfile.txt" двумя объектами - запись из двух разных экземпляров
        double d = 288.36;
        int i = 1230;

        // Конвертировать в массивы bytes[]
        byte[] AD = BitConverter.GetBytes(d);
        byte[] AI = BitConverter.GetBytes(i);

        // Записать в файл число d типа double из объекта fs
        fw1.Write(AD, 0, AD.Length);

        // Записать в файл число i типа int из объекта fs2
        fw2.Seek(0, SeekOrigin.End); // не забыть за прокрутку в конец файла
        fw2.Write(AI, 0, AI.Length); // записать целое число

        // Закрыть файлы после использования
        fw1.Close();
        fw2.Close();
      }
      catch (UnauthorizedAccessException e)
      {
        // Обработка, если файл присутствует и он скрытый (hidden)
        Console.WriteLine("Error: " + e.Message);
      }
      catch (FileNotFoundException e)
      {
        // Обработка, если файл не найден
        Console.WriteLine("Error: " + e.Message);
      }

      // Чтение из файла данных разными объектами
      try
      {
        // Первый объект - будет считывать число типа double
        FileStream fr1 = new FileStream("myfile.txt", FileMode.Open, FileAccess.Read, FileShare.Read);

        // Второй объект - будет считывать число типа int из того же файла, что и fr1
        FileStream fr2 = new FileStream("myfile.txt", FileMode.Open, FileAccess.Read, FileShare.Read);

        // Считать число типа double из объекта fr1
        byte[] AD = new byte[sizeof(double)]; // выделить память для типа double
        double d; // результат

        // Считать побайтно - метод ReadByte(),
        // вместо цикла можно также использовать fr1.Read(AD, 0, AD.Length)
        for (int i = 0; i < AD.Length; i++)
          AD[i] = (byte)fr1.ReadByte();

        d = BitConverter.ToDouble(AD);
        Console.WriteLine("d = {0}", d);

        // Считать число типа int из другого объекта fr2
        int t;
        byte[] AI = new byte[sizeof(int)];

        // Считать в массив AI, учесть смещение,
        // перемотать вперед на размер типа double
        fr1.Seek(sizeof(double), SeekOrigin.Begin);

        // считать число типа int как массив byte[]
        fr1.Read(AI, 0, AI.Length);

        // конвертировать byte[] => int
        t = BitConverter.ToInt32(AI);
        Console.WriteLine("t = {0}", t);

        fr1.Close();
        fr2.Close();
      }
      catch (FileNotFoundException e)
      {
        // Обработка ошибки, если файл не найден
        Console.WriteLine("Error: " + e.Message);
      }
    }
  }
}

 

3.3.4. Пример поочередной записи и чтения данных из одного файла. Демонстрация доступа FileShare.ReadWrite

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

using System;
using System.IO;

namespace ConsoleApp10
{
  class Program
  {
    static void Main(string[] args)
    {
      // Доступ к одному файлу из двух разных экземпляров:
      // - экземлпяр fWrite - записывает в файл число;
      // - экземпляр fRead - считывает записанное число из файла и виводит его на экран.
      try
      {
        // Указать, что запись в файл "myfile.dat" будет несколькими объектами - FileShare.ReadWrite
        FileStream fWrite = new FileStream("myfile.dat", FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);

        // Создать другой объект, который будет считывать из этого же файла,
        // обязательно нужно указать FileShare.ReadWrite
        FileStream fRead = new FileStream("myfile.dat", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

        // Использование файла "myfile.dat" двумя объектами - fWrite та fRead

        // Цикл записи чисел 5, 10, 15 в файл объектом и их считывание другим экземпляром
        byte[] A; // дополнительный массив
        int num; // записываемое число
        int offset; // смещение в файле
        A = new byte[sizeof(int)]; // буфер для одного числа типа int
        byte[] A2;

        for (int i = 0; i < 3; i++)
        {
          // 1. Сформировать число
          num = (i + 1) * 5;

          // 2. Записать число в файл "myfile.dat"
          // 2.1. Конвертировать num => в массив byte[]
          A = BitConverter.GetBytes(num);

          // 2.2. Вычислить смещение для записи в файл
          offset = 0 + i * sizeof(int);

          // 2.3. Перемотать текущую позицию записи на смещение offset
          fWrite.Seek(offset, SeekOrigin.Begin);

          // 2.4. Записать num в файл
          fWrite.Write(A, 0, A.Length);

          // 3. Сразу прочитать записанное число из файла
          // 3.1. Выделить память для буфера типа byte[], использовать дополнительную переменную A
          A2 = new byte[sizeof(int)];

          // 3.2. Вычислить смещение
          offset = 0 + i * sizeof(int);

          // 3.3. Перемотать текущую позицию чтения на offset байт
          fRead.Seek(offset, SeekOrigin.Begin);

          // 3.4. Прочитать число в буфер A2
          fRead.Read(A2, 0, A2.Length);

          // 3.5. Конвертировать byte[] => int
          int num2 = BitConverter.ToInt32(A2);

          // 3.6. Вывести число num2
          Console.WriteLine("{0} ", num2);
        }

        // Закрыть файлы после использования
        fWrite.Close();
        fRead.Close();
      }
      catch (FileNotFoundException e)
      {
        // Обработка, если файл не найден
        Console.WriteLine("Error: " + e.Message);
      }
    }
  }
}

Результат выполнения программы

5
10
15

 


Связанные темы