.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 streamBinaryWriter
: 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:
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:
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
Method | Description |
---|---|
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:
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:
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
Method | Description |
---|---|
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.
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:
- File Formats: Creating and reading custom binary file formats
- Game Development: Saving game state and loading assets
- Network Communication: Transmitting data efficiently between systems
- Database Systems: Storing and retrieving data in binary formats
- Multimedia Applications: Processing audio, video, and image files
Best Practices
When working with binary I/O, consider these best practices:
- Always use
using
statements for proper resource disposal - Handle exceptions appropriately, especially
IOException
- Consider endianness (byte order) when sharing binary data across systems
- Document your binary format if creating custom file types
- Include version information in your binary formats to support future changes
- Consider compression for large binary files
BinaryReader vs. TextReader
Feature | BinaryReader | TextReader |
---|---|---|
Data Format | Binary data | Text data |
Efficiency | More efficient | Less efficient |
Types Support | Direct support for primitive types | Strings only (conversions needed) |
Use Case | Raw data, structured data | Human-readable text |
Common Pitfalls
- Not closing streams properly: Can lead to resource leaks
- Ignoring file format version: Makes it difficult to update file formats
- Cross-platform issues: Byte ordering differences (endianness)
- No error checking: Missing proper exception handling
- 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
- Microsoft Documentation: BinaryReader Class
- Microsoft Documentation: BinaryWriter Class
- .NET File System and the Registry
- C# Programming Guide
Exercises
- Create a program that saves and loads a list of products (with ID, name, price, and quantity) to a binary file.
- Extend the student example to include an array of course grades for each student.
- Create a simple file encryption tool that uses
BinaryReader
andBinaryWriter
to encrypt and decrypt files using a simple XOR cipher. - Implement a program that can merge multiple binary files into one file.
- 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! :)