o7planning

C# Streams tutorial - binary streams in C#

  1. Stream Overview
  2. The basic examples for Stream
  3. FileStream
  4. BufferedStream
  5. MemoryStream
  6. UnmanagedMemoryStream
  7. CryptoStream

1. Stream Overview

Stream is a class that simulates a stream of bytes to be lined up in a row. Such as the transmission of data on the network, data transmited are contiguous stream of bytes from the first byte to the last byte.
Stream is a base class, the other stream extend from this class. There are several classes have been built in C#, which expanded from Stream class for different purposes, such as:
Class
Description
BufferedStream
A utility stream, it wrapped another stream, which helps improve performance
FileStream
Stream used to read and write data to the file.
MemoryStream
Creates a stream whose backing store is memory.
UnmanagedMemoryStream
IsolatedStorageFileStream
PipeStream
NetworkStream
CryptoStream
Defines a stream that links data streams to cryptographic transformations.
DeflateStream
GZipStream
Stream is an abstract class, it can not initialize an object by itself, you can initialize a Stream object from the Constructors of the subclass. Stream class provides the basic methods of working with data streams, namely the method of read/write a byte or an array of bytes.
Depending on the stream, there are the stream that supports both read and write, and seek by moving the cursor on the stream, read and write data at the cursor position.
The properties of the Stream:
Attribute
Description
CanRead
Gets a value indicating whether the current stream supports reading.
CanSeek
Gets a value indicating whether the current stream supports seeking.
CanWrite
Gets a value indicating whether the current stream supports writing.
Length
Gets the length in bytes of the stream.
Position
Gets or sets the current position of this stream.
The methods of the Stream:

2. The basic examples for Stream

You can write each byte or an array of bytes to the stream. And when reading, you can read each byte or multi bytes and assigned to a temporary array.
A byte is 8-bit, in which a bit is 0 or 1. Thus 1 byte corresponds to a number from 0 to 255 (2 ^ 8-1).
Write stream example
And now let's start with a simple example, create a Stream that write data to File. You can write each byte into stream or write an array of bytes to the 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 via Constructor of 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)
                // This array corresponds 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();

        }

    }
}
Running the example:
Note: In the ASCII character encoding, each CSII character corresponds to a number <256.
CharacterValueCharacterValue
H72W87
e101r114
l108d100
o11132
!33
You can refer to the ASCII code table at:
Write stream example
The example above you have to write data to the file C:\temp\MyTest.txt, now you can write an example to read data from that file.
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();
        }
    }

}
Running the example:

3. FileStream

Filestream is a class which extends from the Stream class, FileStream is used to read and write data to file, it inherits the properties, methods of Stream, and has additional functions for reading and recording data on the file.
There are some read-write mode:
FileMode
Description
Append
Opens the file if it exists and seeks to the end of the file, or creates a new file
Create
Specifies that the operating system should create a new file. If the file already exists, it will be overwritten.
CreateNew
Specifies that the operating system should create a new file.If the file already exists, an IOException exception is thrown. This mode requires FileIOPermissionAccess.Write permission.
Open
Specifies that the operating system should open an existing file. A System.IO.FileNotFoundException exception is thrown if the file does not exist.
OpenOrCreate
Specifies that the operating system should open a file if it exists; otherwise, a new file should be created
Truncate
Specifies that the operating system should open an existing file. When the file is opened, it should be truncated so that its size is zero bytes.
FileMode example:
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();
        }
    
    }
}
Running the example:
With FileMode.Append, data will be appended to the file, if the file have already existed:
Constructor:
There are 11 constructors on FileStream class (Excluding the constructor obsoleted) used to initialize a FileStream object:
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)
However, you also have other ways to create a FileStream object, such as through the FileInfo, this is the class that represents a file in the system.
Methods of FileInfo, returns FileStream
Description
Create()
By default, full read/write access to new files is granted to all users.
Open(FileMode)
Opens a file in the specified mode.
Open(FileMode, FileAccess)
Opens a file in the specified mode with read, write, or read/write access.
Open(FileMode, FileAccess, FileShare)
Opens a file in the specified mode with read, write, or read/write access and the specified sharing option.
OpenWrite()
Creates a write-only FileStream.
OpenRead()
Creates a read-only FileStream.
Create FileStream via 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 is a class which extends from the Stream class, it's a buffer stream which wrap a different stream and help to improve the efficiency of reading and writting data.
BufferedStream only two constructors, it wrap a different Stream.
Constructor
Description
BufferedStream(Stream)
Initializes a new instance of the BufferedStream class with a default buffer size of 4096 bytes.
BufferedStream(Stream, Int32)
Initializes a new instance of the BufferedStream class with the specified buffer size.
I put out a situation, you create a BufferedStream wrap FileStream, for the purpose of writing data to the file. The data written to the stream buffer will be located temporarily in memory, and when the buffer is full, the data is automatically flushed to file, you can proactively flush data to the file by using the Flush() method. Using BufferedStream in this case reduces the number of times to write down the drive, and so it increases the efficiency of the program.
For example, one BufferedStream wrap a stream to write data to file:
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();
        }
    }

}
Results when running examples:

5. MemoryStream

MemoryStream class is directly extended from the Stream class, it is the stream which data is stored on the memory.
Essentially, MemoryStream is an object that manages a buffer is an array of bytes, while the bytes are written to this stream will automatically be assigned to the next position from the current position of the cursor on the array. When the buffer is full a new array with a larger size to be created, and copy the data from the old array.
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)
Example:
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 position.
            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();

             
        }
    }

}
Running the example:

6. UnmanagedMemoryStream

UnmanagedMemoryStream lets you read the data stream that is not managed without copying all of them to manage the Heap memory before using. It helps you save memory if you are having to deal with lots of data.

Note that there is a limit of 2GB for MemoryStream so you have to use the UnmanagedMemoryStream if you exceed this limit.
I put out a situation: There is discrete data available on the memory. And you can gather to manage by UnmanagedMemoryStream by managing the pointers of the aforementioned discrete data, instead of copying them to the stream to manage.
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 is a class used for data stream encryption.
The image below illustrates CryptoStream wrap another stream (such as writting stream file), when you write byte data to CryptoStream, bytes will be encrypted into another bytes before flushing to stream that is written to the file . Now the content of the file has been encrypted.

Note that you can choose an encryption algorithm when creating CryptoStream object.
In a reverse situation, CryptoStream wrap a reading stream file (file whose contents were encrypted above), the byte in FileStream has been encrypted, it will be decrypted by CryptoStream.
Another important thing you need to remember is that, not all of encryption algorithm have 2-way encryption and decryption solutions.
Let's look at an example:

Here I use the DES algorithm to encrypt and decrypt, you need to provide 128-bit array which is the key of your security.
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();
View full example.
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.
                // (FileStream in this case used to write the file).
                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.
            using (FileStream stream = new FileStream(encryedFile, FileMode.Open, FileAccess.Read))
            {
                // Decryptor.
                ICryptoTransform decryptor = provider.CreateDecryptor();

                // A CryptoStream wrap to FileStream.
                // (FileStream in this case used to read the file).
                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();
        }
    }

}
Running the example:
View the contents of the newly created file.