Руководство CSharp Streams - двоичные потоки в C#

1- Обзор Stream

Stream - класс, который имитирующий поток byte (байтов), выстроенные в ряд. Например, передача данных в сети, передаваемые данные - это непрерывный поток байтов от первого байта до последнего байта.
Stream - это базовый класс, другие потоки (stream) расширенные от этого класса. В C# существует несколько классов, которые расширены из класса Stream для различных целей, таких как:
Class Описание
BufferedStream Утилитарный поток, обернутый (wrap) другим потоком, который помогает повысить эффективность.
FileStream Поток использующийся для чтения записи данных в файл.
MemoryStream Поток работающий с данными в памяти.
UnmanagedMemoryStream
IsolatedStorageFileStream
PipeStream
NetworkStream
CryptoStream Поток читает записывает зашифрованные данные.
DeflateStream
GZipStream
Stream является абстрактным классом, он не может инициализировать объект сам, вы можете инициализировать объект Stream из конструкторов подкласса. Класс Stream предоставляет базовые методы работы с потоками данных, а именно метод чтения / записи байта или массив байтов.
В зависимости от потока есть поток, который поддерживает как чтение, так и запись, а также seek (поиск) путем перемещения курсора в потоке, читает и записывает данные в позиции курсора.
Свойства (property)  Stream:
Свойства Описание
CanRead Свойство дает знать, поддерживает ли текущий поток чтение.
CanSeek Свойство дает знать, поддерживает ли текущий поток поиск.
CanWrite Свойство дает знать, поддерживает ли текущий поток запись.
Length Возвращает длину потока в байтах.
Position Текущая позиция курсора в потоке.
Методы Stream:

2- Базовй пример Stream

С  Stream ​​​​​​​в ы можете записать в поток каждый байт или массив байтов. И при чтении вы можете читать каждый байт или несколько байтов и прикрепленные к  временному массиву.
Один byte равен 8 bit, в котором bit равен 0 или 1. Таким образом, 1 byte соответствует числу от 0 до 255 (2 ^ 8-1).

2.1- Пример записи данных потока

А теперь давайте начнем с простого примера, создайте Stream, который записывает данные в файл. Вы можете записать каждый байт в поток или записать массив байтов в поток.
StreamWriteDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace CSharpStreamsTutorial
{
  class StreamWriteDemo
  {
      public static void Main(string[] args)
      {
          string path = @"C:\temp\MyTest.txt";

          // Create directory
          Directory.CreateDirectory(@"C:\temp");

          // Create Stream object from subclass FileStream
          // FileMode.Create: Create file, if it already exists, it will be overwritten.
          Stream writingStream = new FileStream(path, FileMode.Create);

          try
          {
              // A byte array. (1byte < 2^8)
              // Corresponding to  {'H','e','l','l','o',' ','W','o','r','l','d'}
              byte[] bytes = new byte[] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100 };

              if (writingStream.CanWrite)
              {
                  writingStream.Write(bytes, 0, bytes.Length);
               
                  // Write a byte (33 = '!')
                  writingStream.WriteByte(33);
              }
          }
          catch (Exception e)
          {
              Console.WriteLine("Error:" + e);
          }
          finally
          {
              // Close Stream, free resources.
              writingStream.Close();
          }
          Console.ReadLine();

      }

  }
}
Запуск примера:
Примечание: В таблице кодировке символов ASCII каждый символ CSII соответствует числу  < 256.
Character Value   Character Value
H 72   W 87
e 101   r 114
l 108   d 100
o 111     32
! 33      
Вы можете изучить больше про кодовую таблицу  ASCII по ссылке:

2.2- Пример чтения данных потока

В примере выше вы должны записать данные в файл C:\temp\MyTest.txt, теперь вы можете написать поток для чтения данных из этого файла.
StreamReadDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace CSharpStreamsTutorial
{
    class StreamReadDemo
    {
        public static void Main(string[] args)
        {
            String path = @"C:\temp\MyTest.txt";

            if (!File.Exists(path))
            {
                Console.WriteLine("File " + path + " does not exists!");
                return;
            }

            // Create Stream object via constructor of FileStream
            // FileMode.Open: Open file to read.
            using (Stream readingStream = new FileStream(path, FileMode.Open))
            {
                byte[] temp = new byte[10];
                UTF8Encoding encoding = new UTF8Encoding(true);

                int len = 0;

                // Read on stream and asign to temporary array.
                // (Asign to elements of array from index 0, and max temp.Length elements)
                // Returns the number of bytes read.
                while ((len = readingStream.Read(temp, 0, temp.Length)) > 0)
                {
                    // Converts to string
                    // ('len' elements from index 0).
                    String s = encoding.GetString(temp, 0, len);
                    Console.WriteLine(s);
                }
            }

            Console.ReadLine();
        }
    }

}
Запуск примера:

3- FileStream

Filestream - это класс, который расширен из класса Stream, FileStream используется для чтения и записи данных в файл, он наследует свойства (property), методы Stream и одновременно имеет отдельные функции для чтения и записи данных в файл.
Есть несколько режимов чтения-записи данных в файл:
FileMode Описание
Append Открыть файл если он уже существует, перемещает сурсор в конец файла для продолжения записи в файл, если не существует, он будет создан.
Create Говорит операционной системе создать новый файл. Если папка существует, то будет переопределена.
CreateNew Говорит операционной система создать новый файл. Если файл уже существует, выбросится исключение IOException. Этот мод требует авторизацию FileIOPermissionAccess.Write
Open Говорит операционной системе открыть существующий файл.  Если файл не существует, выбросится исключениеSystem.IO.FileNotFoundException
OpenOrCreate Говорит операционной системе открыть файл если он существует; если нет, то создается новый файл.
Truncate Говорит операционной системе открыть существующий файл. Когда откроется файл, он будет отрезан так, что содержание возвращается к 0 byte.
​​​​​​​Пример с  FileMode:
FileStreamFileModeDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace CSharpStreamsTutorial
{
    class FileStreamFileModeDemo
    {
        public static void Main(string[] args)
        {
            String path = @"C:\temp\MyTest.txt";

            if (!File.Exists(path))
            {
                Console.WriteLine("File " + path + " does not exists!");

                // Ensure that the parent directory exists.
                Directory.CreateDirectory(@"C:\temp");
            }

            // Create a FileStream to write file.
            // (FileMode.Append: Open file to write to end of file,
            // if file does not exit, create it and write).

            using (FileStream writeFileStream = new FileStream(path, FileMode.Append) )
            {
                string s = "\nHello every body!";

                // Convert string to bytes with UTF8 encoding.
                byte[] bytes = Encoding.UTF8.GetBytes(s);

                // Write bytes to file.
                writeFileStream.Write(bytes, 0, bytes.Length);
            }
            Console.WriteLine("Finish!");

            Console.ReadLine();
        }
    
    }
}
Запуск примера:
С FileMode.Append данные будут добавлены в файл, если файл уже существует:

Constructor:

В классе FileStream имеется 11 constructor (за исключением устаревших constructor), используемых для инициализации объекта FileStream:
FileStream Constructors
FileStream(SafeFileHandle, FileAccess)     

FileStream(SafeFileHandle, FileAccess, Int32)

FileStream(SafeFileHandle, FileAccess, Int32, Boolean)     

FileStream(String, FileMode)

FileStream(String, FileMode, FileAccess)

FileStream(String, FileMode, FileAccess, FileShare)

FileStream(String, FileMode, FileAccess, FileShare, Int32)

FileStream(String, FileMode, FileAccess, FileShare, Int32, Boolean)

FileStream(String, FileMode, FileAccess, FileShare, Int32, FileOptions)     

FileStream(String, FileMode, FileSystemRights, FileShare, Int32, FileOptions)

FileStream(String, FileMode, FileSystemRights, FileShare, Int32, FileOptions, FileSecurity)
Однако у вас также есть другие способы создания объекта FileStream, например, через FileInfo, это класс, который представляет файл в системе.
Методы FileInfo возвращающие FileStream. Описание
Create() По умолчанию, весь доступ к чтению записи нового файла будет прикреплен ко всем пользователям.
Open(FileMode)    Открыть файл в указанном режиме.
Open(FileMode, FileAccess)    Открыть файл в указанном режиме чтения записи, или авторизацией чтения записи.
Open(FileMode, FileAccess, FileShare) Открыть файл в указанном режиме чтения записи, или авторизацией чтения записи, и опциями разделения.
OpenWrite() Создать FileStream только для записи данных.
OpenRead() Создать FileStream только для чтения данных.
Смотрите так же:
Cоздать Filestream через FileInfo:
FileStreamFileInfoDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace CSharpStreamsTutorial
{
   class FileStreamFileInfoDemo
   {

       public static void Main(string[] args)
       {
           FileInfo afile = new FileInfo(@"C:\temp\MyTest.txt");

           if (afile.Exists)
           {
               Console.WriteLine("File does not exist!");
               Console.Read();
               return;
           }
           // Open file and truncate all bytes.
           using (FileStream stream = afile.Open(FileMode.Truncate))
           {
               String s = "New text";

               byte[] bytes = Encoding.UTF8.GetBytes(s);

               stream.Write(bytes, 0, bytes.Length);
           }

           Console.WriteLine("Finished!");
           Console.Read();

       }
   }

}

4- BufferedStream

BufferedStream - это класс, который расширен из класса Stream, это поток (stream), который обертывает (wrap) другой поток и помогает повысить эффективность чтения и записи данных.
BuffedStream имеет только двa конструктора (constructor), он обертывает другой поток (stream).
Конструктор (Constructor) Описание
BufferedStream(Stream) Инициализирует объект BufferedStream с размером буфера по умолчанию 4096 байт.
BufferedStream(Stream, Int32) Инициализирует объект​​​​​​​ BufferedStream с указанным размером буфера.
Я создаю ситуацию, вы создаете BufferedStream обертывающий FileStream для записи данных в файл. Данные, записанные в буфер потока, будут временно размещены в памяти, а когда буфер будет заполнен, данные автоматически сбрасываются (Flush) в файл, вы можете сами сбросить данные в файл с помощью метода Flush(). Использование BufferedStream в этом случае уменьшает частоту необходимости записывать на диск, и, следовательно, повышает эффективность программы.
Примером ниже является  BufferedStream обертывающий поток записи данных в файл:
BufferedStreamWriteFileDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace CSharpStreamsTutorial
{
    class BufferedStreamWriteFileDemo
    {
        public static void Main(string[] args)
        {
            String fileName = @"C:\temp\MyFile.txt";

            FileInfo file = new FileInfo(fileName);

            // Make sure the directory exists.
            file.Directory.Create();

            // Create a new file, if it exists it will be overwritten.
            // Returns FileStream object.
            using (FileStream fileStream = file.Create())
            {
                // Create BufferedStream wrap the FileStream.
                // (Specify the buffer is 10000 bytes).
                using (BufferedStream bs = new BufferedStream(fileStream, 10000))
                {
                    int index = 0;
                    for (index = 1; index < 2000; index++)
                    {
                        String s = "This is line " + index + "\n";

                        byte[] bytes = Encoding.UTF8.GetBytes(s);

                        // Write to buffer, when the buffer is full it will
                        // automatically push down the file.
                        bs.Write(bytes, 0, bytes.Length);
                    }

                    // Flushing the remaining data in the buffer to the file.
                    bs.Flush();
                }
                
            }

            Console.WriteLine("Finished!");
            Console.Read();
        }
    }

}
Результат запуска примера:

5- MemoryStream

Класс MemoryStream непосредственно расширен из класса Stream, это поток (stream), данные которого хранятся (store) в памяти.
По сути, MemoryStream - это объект, который управляет буфером (buffer), - это массив байтов, когда байты записываются в этот поток, они автоматически будет прикреплены к следующей позиции от текущей позиции курсора в массиве. Когда буфер заполнен, создается новый массив большего размера и копируются данные из старого массива.

Constructor:

MemoryStream()   
MemoryStream(Byte[] buffer)
MemoryStream(Byte[] buffer, Boolean writable)
MemoryStream(Byte[] buffer, Int32 index, Int32 count, Boolean writable) 
MemoryStream(Byte[] buffer, Int32 index, Int32 count, Boolean, Boolean publiclyVisible)  
MemoryStream(Byte[], Int32, Int32, Boolean, Boolean)  
 MemoryStream(Int32 capacity)
Пример:
MemoryStreamDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace CSharpStreamsTutorial
{
    class MemoryStreamDemo
    {
        static void Main()
        {
            // Create MemoryStream object with capacity of 100 bytes.
            MemoryStream memoryStream = new MemoryStream(100);

            byte[] javaBytes = Encoding.UTF8.GetBytes("Java");
            byte[] csharpBytes = Encoding.UTF8.GetBytes("CSharp");

            // Write bytes to memory stream.
            memoryStream.Write(javaBytes, 0, javaBytes.Length);
            memoryStream.Write(csharpBytes, 0, csharpBytes.Length);

            // Write out capacity and length.
            // ==> Capacity: 100, Length: 10.
            Console.WriteLine("Capacity: {0} , Length: {1}",
                                  memoryStream.Capacity.ToString(),
                                  memoryStream.Length.ToString());

            // At this time the cursor position is standing after the character 'p'.
            // ==> 10.
            Console.WriteLine("Position: "+ memoryStream.Position);

            // Move the cursor backward 6 bytes, compared to the current location
            memoryStream.Seek(-6, SeekOrigin.Current);

            // Now cursor position after the character 'a' and before 'C'.
            // ==> 4
            Console.WriteLine("Position: " + memoryStream.Position);

            byte[] vsBytes = Encoding.UTF8.GetBytes(" vs ");

            // Write to memory stream.
            memoryStream.Write(vsBytes, 0, vsBytes.Length);


            byte[] allBytes = memoryStream.GetBuffer();

            string data = Encoding.UTF8.GetString(allBytes);

            // ==> Java vs rp
            Console.WriteLine(data);

            Console.WriteLine("Finish!");
            Console.Read();

             
        }
    }

}
Запуск примера:

6- UnmanagedMemoryStream

UnmanagedMemoryStream позволяет вам читать поток данных, который не управляется, без необходимости копирования всего на управление памятью Heap перед использованием. Это помогает вам экономить память, если вам приходится иметь дело с большим количеством данных.

Обратите внимание, что для MemoryStream существует ограничение в 2 ГБ, поэтому вы должны использовать UnmanagedMemoryStream, если превысили этот предел.
Я исхожу из ситуации: в памяти есть дискретные данные. И вы можете их собрать, чтобы управлять с помощью  UnmanagedMemoryStream, управляя указателями (pointer) вышеупомянутых дискретных данных, вместо того, чтобы копировать их в поток (stream) для управления.

Constructor:

UnmanagedMemoryStream()
UnmanagedMemoryStream(Byte* pointer, Int64 length)
UnmanagedMemoryStream(Byte* pointer, Int64 length, Int64 capacity, FileAccess access)
UnmanagedMemoryStream(SafeBuffer buffer, Int64 offset, Int64 length)  
UnmanagedMemoryStream(SafeBuffer buffer, Int64 offset, Int64 length, FileAccess access)
  • TODO

7- CryptoStream

CryptoStream - класс, используемый для шифрования потока данных.
На приведенном ниже изображении показано, как CryptoStream обертывает другой поток (например, создает поток записи файла), когда вы записываете байтовые данные в CryptoStream, эти байты (bytes) будут зашифрованы в другие байты перед сбросом в поток, который записывается в файл. Теперь содержимое файла было зашифровано.

Обратите внимание, что вы можете выбрать алгоритм шифрования при создании объекта CryptoStream.
В обратной ситуации, поток  CryptoStream обертывает поток чтения файла (файл, содержимое которого было зашифровано выше), байты в  потоке FileStream были зашифрованы (encrypted), и будут дешифрован (decrypt) используя  CryptoStream.
Еще одна важная вещь, которую вы должны запомнить, заключается в том, что не все алгоритмы шифрования имеют двусторонние способы шифрования и дешифрования.
Давайте рассмотрим пример:

Здесь я использую алгоритм DES для шифрования и дешифрования, вам необходимо предоставить 128-битный массив, который является ключом вашей безопасности.
DES Algorithm
// Objects provides the DES encryption algorithm.
DESCryptoServiceProvider provider = new DESCryptoServiceProvider();

// Use your private key (Must be 128bit = 8byte).
// (Equivalent to 8 ASCII characters)
provider.Key = ASCIIEncoding.ASCII.GetBytes("1234abcd");
provider.IV = ASCIIEncoding.ASCII.GetBytes("12345678");


//  Encryptor
ICryptoTransform encryptor = provider.CreateEncryptor();


// Decrytor.
ICryptoTransform decryptor = provider.CreateDecryptor();
Смотрите полный пример.
CryptoStreamExample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;

namespace CSharpStreamsTutorial
{
    class CryptoStreamExample
    {
        public static void Main(string[] args)
        {
            // Provides DES encryption algorithm.
            DESCryptoServiceProvider provider = new DESCryptoServiceProvider();

            // Your secret key (Must by 128bits = 8bytes).
            // (Equivalent to 8 ASCII characters).
            provider.Key = ASCIIEncoding.ASCII.GetBytes("1234abcd");
            provider.IV = ASCIIEncoding.ASCII.GetBytes("12345678");


            String encryedFile = @"C:\temp\EncryptedFile.txt";


            // A stream to write file.
            using (FileStream stream = new FileStream(encryedFile, FileMode.OpenOrCreate, FileAccess.Write))
            {

                // Encryptor.
                ICryptoTransform encryptor = provider.CreateEncryptor();

                // Create CryptoStream wrap the FileStream.
                using (CryptoStream cryptoStream = new CryptoStream(stream,
                                     encryptor, CryptoStreamMode.Write))
                {
                    // A byte array is not encrypted.
                    byte[] data = ASCIIEncoding.ASCII.GetBytes("Bear, I love you. OK?");

                    // Write to cryptoStream.
                    cryptoStream.Write(data, 0, data.Length);
                }

            }
            Console.WriteLine("Write to file: " + encryedFile);

            // Next read the encrypted file has been created above.

            // A Stream to read file.
            using (FileStream stream = new FileStream(encryedFile, FileMode.Open, FileAccess.Read))
            {
                // Decryptor.
                ICryptoTransform decryptor = provider.CreateDecryptor();

                // A CryptoStream wrap to FileStream.
                using (CryptoStream cryptoStream = new CryptoStream(stream,
                                     decryptor, CryptoStreamMode.Read))
                {
                    byte[] temp = new byte[1024];
                    int read=0;
                    while((read =cryptoStream.Read(temp,0,temp.Length )) >0 )
                    {
                        String s= Encoding.UTF8.GetString(temp,0,read);

                        Console.Write(s);
                    }
                }
            }

            // Finished
            Console.Read();
        }
    }

}
Запуск примера:
Просмотрите содержимое созданного файла.