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. Cтворення файлу. Найпростіший випадок. Приклад

Щоб створити новий файл, потрібно задати режим створення файлу 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

 


Зв’язані теми