Handling Binary File Operations with C# FileStream

The FileStream class enables direct byte-level interaction with files stored on disk or network locations. Unlike StreamReader or StreamWriter, which handle character data, FileStream works with raw bytes. This capability is essential for random access scenarios where the application must jump to specific positions within a file rather than processing data sequentially.

Instantiating a stream requires specifying the target path and a FileMode enumeration value. The mode dictates whether the file is created, opened, or truncated. Common modes include Open, which requires the file to exist; Create, which overwrites existing content; and Append, which positions the cursor at the end for writing. Access rights are controlled via the FileAccess enumeration, allowing Read, Write, or ReadWrite permissoins. If access is not specified, ReadWrite is assumed.

Helper methods such as File.OpenRead simplify creation for specific scenarios. For instance, File.OpenRead("data.txt") equates to creating a FileStream with FileMode.Open and FileAccess.Read. Similarly, FileInfo instances can expose stream objects through their OpenRead methods.

Navigation within the stream is managed through the Seek method. This function accepts an offset and a SeekOrigin value. The origin defines the reference point: Begin starts from the zero byte, Current uses the active position, and End calculates from the file's tail. Negative offsets are permissible when using SeekOrigin.End to locate data near the conclusion of the file. Every read or write operation automatically advances the internal pointer.

Reading data involves allocating a byte array and passing it to the Read method. The method returns the number of bytes actually retrieved. Since FileStream handles bytes, converting this data to text requires an Encoding object. A Decoder transforms the byte buffer into characters based on a specific scheme like UTF8.

Writing follows a similar pattern but in reverse. Text must first be converted into a byte array using an Encoder. The Write method then transfers these bytes to the stream at the current pointer location. Both operations should be wrapped in try-catch blocks to handle potential IOExceptions arising from permission issues or missing files.

Basic File Interaction

The following example demonstrates appending binary data to a file and subsequently reading the entire content back into memory.

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

class FileOperation
{
    static void Main()
    {
        string content = "Initial data payload";
        byte[] dataBuffer = Encoding.UTF8.GetBytes(content);
        string path = "sample.dat";

        using (FileStream output = new FileStream(path, FileMode.Append, FileAccess.Write))
        {
            output.Write(dataBuffer, 0, dataBuffer.Length);
        }

        using (FileStream input = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            int length = (int)input.Length;
            byte[] readBuffer = new byte[length];
            int bytesRead = input.Read(readBuffer, 0, length);
            
            string result = Encoding.UTF8.GetString(readBuffer, 0, bytesRead);
            Console.WriteLine(result);
        }
    }
}

Random Access Reading

To access specific segments of a file without loading the entire content, use the Seek method to reposition the stream cursor. The example below opens a source file, moves to a specific offset, and decodes a segment of bytes.

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

class RandomAccessReader
{
    static void Main()
    {
        byte[] buffer = new byte[100];
        char[] charBuffer = new char[100];
        string targetFile = "../../source_code.txt";

        try
        {
            using (FileStream stream = new FileStream(targetFile, FileMode.Open))
            {
                int offset = 50;
                stream.Seek(offset, SeekOrigin.Begin);
                int count = stream.Read(buffer, 0, buffer.Length);

                Decoder decoder = Encoding.UTF8.GetDecoder();
                decoder.GetChars(buffer, 0, count, charBuffer, 0);

                Console.WriteLine(new string(charBuffer, 0, count));
            }
        }
        catch (IOException ex)
        {
            Console.WriteLine($"Access failed: {ex.Message}");
        }
    }
}

Encoding and Writing Data

Writing text to a binary stream requires converting characters into bytes. An Encoder object handles this transformation, ensuring the correct byte representation is written to the disk.

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

class RandomAccessWriter
{
    static void Main()
    {
        string message = "Writing specific segment.";
        char[] charData = message.ToCharArray();
        byte[] byteData = new byte[charData.Length];
        string outputPath = "output_log.txt";

        try
        {
            using (FileStream stream = new FileStream(outputPath, FileMode.Create))
            {
                Encoder encoder = Encoding.UTF8.GetEncoder();
                encoder.GetBytes(charData, 0, charData.Length, byteData, 0, true);

                stream.Seek(0, SeekOrigin.Begin);
                stream.Write(byteData, 0, byteData.Length);
            }
        }
        catch (IOException ex)
        {
            Console.WriteLine($"Write operation failed: {ex.Message}");
        }
    }
}

The Write method accepts the byte array, the starting index within that array, and the total number of bytes to commit. When using an Encoder, the final parameter in GetBytes indiactes whether the internal state should be flushed. Setting this to true ensures the encoder releases resources and completes the conversion process immediately.

Tags: C# file-io FileStream .NET programming

Posted on Sat, 18 Jul 2026 16:26:34 +0000 by jeremyphphaven