Skip to main content

.NET BinaryReader & BinaryWriter

Working with binary data is a fundamental skill for many programming tasks, from file processing to network communication. .NET provides specialized tools for handling binary data through the BinaryReader and BinaryWriter classes.

Introduction to Binary I/O

Binary I/O (Input/Output) deals with reading and writing data in its binary form rather than as text. This approach offers several advantages:

  • Efficiency: Binary data typically takes less space than text representations
  • Precision: No conversion issues with floating-point numbers
  • Speed: Faster reading and writing of primitive data types
  • Structure preservation: Maintains the exact format of complex data structures

The .NET Framework provides two key classes for binary I/O operations:

  • BinaryReader: Reads primitive data types as binary values from a stream
  • BinaryWriter: Writes primitive data types in binary format to a stream

BinaryReader Class

The BinaryReader class reads primitive data types from a stream as binary values. It's located in the System.IO namespace.

Creating a BinaryReader

To create a BinaryReader instance, you need to provide a stream:

csharp
using System;
using System.IO;

// Create a file stream
using (FileStream fileStream = new FileStream("data.bin", FileMode.Open))
{
// Create a binary reader from the file stream
using (BinaryReader reader = new BinaryReader(fileStream))
{
// Use the reader here
}
}

Reading Data with BinaryReader

The BinaryReader class provides methods for reading different data types:

csharp
using System;
using System.IO;

using (FileStream fileStream = new FileStream("data.bin", FileMode.Open))
using (BinaryReader reader = new BinaryReader(fileStream))
{
byte byteValue = reader.ReadByte();
int intValue = reader.ReadInt32();
double doubleValue = reader.ReadDouble();
string stringValue = reader.ReadString();
bool boolValue = reader.ReadBoolean();

Console.WriteLine($"Byte: {byteValue}");
Console.WriteLine($"Integer: {intValue}");
Console.WriteLine($"Double: {doubleValue}");
Console.WriteLine($"String: {stringValue}");
Console.WriteLine($"Boolean: {boolValue}");
}

Common BinaryReader Methods

MethodDescription
ReadBoolean()Reads a Boolean value (1 byte)
ReadByte()Reads a single byte
ReadChar()Reads a character (2 bytes)
ReadInt16()Reads a 2-byte signed integer
ReadInt32()Reads a 4-byte signed integer
ReadInt64()Reads an 8-byte signed integer
ReadSingle()Reads a 4-byte floating-point value
ReadDouble()Reads an 8-byte floating-point value
ReadString()Reads a string prefixed with its length
ReadBytes(int count)Reads the specified number of bytes

BinaryWriter Class

The BinaryWriter class writes primitive data types to a stream as binary values. It's also located in the System.IO namespace.

Creating a BinaryWriter

Similar to BinaryReader, you create a BinaryWriter instance by providing a stream:

csharp
using System;
using System.IO;

// Create a file stream
using (FileStream fileStream = new FileStream("output.bin", FileMode.Create))
{
// Create a binary writer from the file stream
using (BinaryWriter writer = new BinaryWriter(fileStream))
{
// Use the writer here
}
}

Writing Data with BinaryWriter

The BinaryWriter class provides methods for writing different data types:

csharp
using System;
using System.IO;

using (FileStream fileStream = new FileStream("output.bin", FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(fileStream))
{
writer.Write((byte)42); // Write a byte
writer.Write(12345); // Write an integer
writer.Write(3.14159); // Write a double
writer.Write("Hello, Binary"); // Write a string
writer.Write(true); // Write a boolean
}

Console.WriteLine("Data written successfully to output.bin");

Common BinaryWriter Methods

MethodDescription
Write(bool)Writes a Boolean value (1 byte)
Write(byte)Writes a single byte
Write(char)Writes a character (2 bytes)
Write(short)Writes a 2-byte signed integer
Write(int)Writes a 4-byte signed integer
Write(long)Writes an 8-byte signed integer
Write(float)Writes a 4-byte floating-point value
Write(double)Writes an 8-byte floating-point value
Write(string)Writes a string prefixed with its length
Write(byte[], int, int)Writes a range from a byte array

Practical Example: Binary File Operations

Let's create a complete example that demonstrates writing and reading structured data to/from a binary file.

Example: Student Records System

We'll create a program that stores and retrieves student records in binary format.

csharp
using System;
using System.IO;
using System.Collections.Generic;

namespace BinaryIOExample
{
class Student
{
public int Id { get; set; }
public string Name { get; set; }
public double GPA { get; set; }
public bool IsActive { get; set; }

public override string ToString()
{
return $"Student ID: {Id}, Name: {Name}, GPA: {GPA}, Active: {IsActive}";
}
}

class Program
{
static void Main(string[] args)
{
string filePath = "students.bin";

// Create sample student data
List<Student> students = new List<Student>
{
new Student { Id = 1001, Name = "Alice Smith", GPA = 3.8, IsActive = true },
new Student { Id = 1002, Name = "Bob Johnson", GPA = 3.2, IsActive = true },
new Student { Id = 1003, Name = "Charlie Brown", GPA = 3.5, IsActive = false }
};

// Write students to binary file
WriteStudentsToBinaryFile(students, filePath);

// Read students from binary file
List<Student> loadedStudents = ReadStudentsFromBinaryFile(filePath);

// Display the loaded students
Console.WriteLine("Students loaded from binary file:");
foreach (var student in loadedStudents)
{
Console.WriteLine(student);
}
}

static void WriteStudentsToBinaryFile(List<Student> students, string filePath)
{
using (FileStream fs = new FileStream(filePath, FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(fs))
{
// First write the count of students
writer.Write(students.Count);

// Then write each student's data
foreach (var student in students)
{
writer.Write(student.Id);
writer.Write(student.Name);
writer.Write(student.GPA);
writer.Write(student.IsActive);
}
}

Console.WriteLine($"Successfully wrote {students.Count} students to {filePath}");
}

static List<Student> ReadStudentsFromBinaryFile(string filePath)
{
List<Student> students = new List<Student>();

using (FileStream fs = new FileStream(filePath, FileMode.Open))
using (BinaryReader reader = new BinaryReader(fs))
{
// Read the count of students
int count = reader.ReadInt32();

// Read each student
for (int i = 0; i < count; i++)
{
Student student = new Student
{
Id = reader.ReadInt32(),
Name = reader.ReadString(),
GPA = reader.ReadDouble(),
IsActive = reader.ReadBoolean()
};

students.Add(student);
}
}

return students;
}
}
}

Output:

Successfully wrote 3 students to students.bin
Students loaded from binary file:
Student ID: 1001, Name: Alice Smith, GPA: 3.8, Active: True
Student ID: 1002, Name: Bob Johnson, GPA: 3.2, Active: True
Student ID: 1003, Name: Charlie Brown, GPA: 3.5, Active: False

Real-World Applications

Binary I/O is used in numerous real-world applications:

  1. File Formats: Creating and reading custom binary file formats
  2. Game Development: Saving game state and loading assets
  3. Network Communication: Transmitting data efficiently between systems
  4. Database Systems: Storing and retrieving data in binary formats
  5. Multimedia Applications: Processing audio, video, and image files

Best Practices

When working with binary I/O, consider these best practices:

  1. Always use using statements for proper resource disposal
  2. Handle exceptions appropriately, especially IOException
  3. Consider endianness (byte order) when sharing binary data across systems
  4. Document your binary format if creating custom file types
  5. Include version information in your binary formats to support future changes
  6. Consider compression for large binary files

BinaryReader vs. TextReader

FeatureBinaryReaderTextReader
Data FormatBinary dataText data
EfficiencyMore efficientLess efficient
Types SupportDirect support for primitive typesStrings only (conversions needed)
Use CaseRaw data, structured dataHuman-readable text

Common Pitfalls

  1. Not closing streams properly: Can lead to resource leaks
  2. Ignoring file format version: Makes it difficult to update file formats
  3. Cross-platform issues: Byte ordering differences (endianness)
  4. No error checking: Missing proper exception handling
  5. Mixing binary and text operations: Can corrupt data

Summary

BinaryReader and BinaryWriter are powerful tools in the .NET framework that allow you to efficiently read and write binary data. They provide type-specific methods that simplify working with various data types in their binary representation.

Key takeaways:

  • BinaryReader reads primitive data types from a stream
  • BinaryWriter writes primitive data types to a stream
  • Binary I/O is more efficient than text I/O for structured data
  • Always use proper resource disposal with using statements
  • Consider the format and compatibility when creating binary files

Additional Resources

Exercises

  1. Create a program that saves and loads a list of products (with ID, name, price, and quantity) to a binary file.
  2. Extend the student example to include an array of course grades for each student.
  3. Create a simple file encryption tool that uses BinaryReader and BinaryWriter to encrypt and decrypt files using a simple XOR cipher.
  4. Implement a program that can merge multiple binary files into one file.
  5. Build a binary file viewer that can display the contents of a binary file in both hexadecimal and ASCII formats.

Happy coding with binary data in .NET!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)