Table of Contents
- Prerequisites
- Overview of C# File I/O Classes
- Reading Text Files
- Writing Text Files
- Reading and Writing Binary Files
- Handling Exceptions in File I/O
- Best Practices for File I/O in C#
- Conclusion
- References
Prerequisites
To follow along, you should:
- Have basic knowledge of C# syntax (variables, classes, methods).
- Understand the .NET ecosystem (e.g., using
Systemnamespaces). - Use an IDE like Visual Studio, Rider, or VS Code with the C# extension.
Overview of C# File I/O Classes
C# provides several classes in the System.IO namespace to handle file operations. Here are the most common:
| Class | Purpose |
|---|---|
File | Static class with methods for reading/writing entire files (e.g., ReadAllText, WriteAllText). |
FileInfo | Instance class for file operations (similar to File, but better for repeated use on the same file). |
StreamReader/StreamWriter | For reading/writing text files line-by-line or in chunks (memory-efficient for large files). |
FileStream | Low-level stream for reading/writing bytes (works with both text and binary files). |
BinaryReader/BinaryWriter | For reading/writing primitive data types (int, double, etc.) to binary files. |
Reading Text Files
Text files (e.g., .txt, .csv) are the most common use case. Let’s explore three methods to read them.
Method 1: File.ReadAllText (Simple Text Reading)
The File.ReadAllText method reads an entire text file into a single string. It’s ideal for small files.
Syntax:
string content = File.ReadAllText(string path, Encoding encoding = Encoding.UTF8);
Example:
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = @"C:\example\test.txt"; // Use @ for verbatim string (avoids escaping backslashes)
try
{
string content = File.ReadAllText(filePath);
Console.WriteLine("File content:\n" + content);
}
catch (FileNotFoundException)
{
Console.WriteLine("Error: File not found.");
}
}
}
Notes:
Encodingspecifies the character encoding (default: UTF-8). UseEncoding.ASCIIorEncoding.Unicodeif needed.- Limitation: Loads the entire file into memory, so avoid for very large files (GBs).
Method 2: File.ReadAllLines (Reading Lines as an Array)
File.ReadAllLines reads a text file into an array of strings, where each element is a line from the file.
Syntax:
string[] lines = File.ReadAllLines(string path, Encoding encoding = Encoding.UTF8);
Example:
string filePath = @"C:\example\lines.txt";
string[] lines = File.ReadAllLines(filePath);
Console.WriteLine("File lines:");
foreach (string line in lines)
{
Console.WriteLine($"- {line}");
}
Use Case: When you need to process lines individually (e.g., parsing CSV data).
Method 3: StreamReader (Advanced Control)
StreamReader reads text files incrementally, making it memory-efficient for large files. It supports reading line-by-line or in chunks.
Key Methods:
ReadLine(): Reads the next line.ReadToEnd(): Reads all remaining content.Dispose(): Releases resources (always call this, or useusing).
Example: Reading Line-by-Line
string filePath = @"C:\example\largefile.txt";
// Use 'using' to auto-dispose StreamReader
using (StreamReader reader = new StreamReader(filePath))
{
string line;
while ((line = reader.ReadLine()) != null) // Read until end of file
{
Console.WriteLine(line); // Process line
}
}
Example: Reading Chunks
using (StreamReader reader = new StreamReader(filePath))
{
char[] buffer = new char[1024]; // Read 1KB chunks
int bytesRead;
while ((bytesRead = reader.Read(buffer, 0, buffer.Length)) > 0)
{
Console.Write(new string(buffer, 0, bytesRead));
}
}
Writing Text Files
Writing text files is as straightforward as reading them. Let’s cover three methods.
Method 1: File.WriteAllText (Simple Text Writing)
File.WriteAllText creates a new file, writes content to it, and closes the file. If the file exists, it overwrites it.
Syntax:
File.WriteAllText(string path, string content, Encoding encoding = Encoding.UTF8);
Example:
string filePath = @"C:\example\output.txt";
string content = "Hello, File I/O in C#!";
File.WriteAllText(filePath, content);
Console.WriteLine("File written successfully.");
Method 2: File.AppendAllText (Appending to Files)
To add content to an existing file (or create a new one if it doesn’t exist), use File.AppendAllText.
Example:
string filePath = @"C:\example\log.txt";
string logEntry = $"[{DateTime.Now}] User logged in.\n";
File.AppendAllText(filePath, logEntry); // Appends to the file
Console.WriteLine("Log entry added.");
Method 3: StreamWriter (Advanced Control)
StreamWriter writes text to a file incrementally, making it ideal for large files or dynamic content.
Key Methods:
Write(string): Writes content without a newline.WriteLine(string): Writes content followed by a newline.Flush(): Forces buffered data to be written to the file.
Example: Writing Line-by-Line
string filePath = @"C:\example\report.txt";
using (StreamWriter writer = new StreamWriter(filePath)) // Overwrites by default
{
writer.WriteLine("Sales Report 2024");
writer.WriteLine("-------------------");
writer.WriteLine("January: $10,000");
writer.WriteLine("February: $15,000");
}
Example: Appending with StreamWriter
To append instead of overwriting, pass true as the second parameter:
using (StreamWriter writer = new StreamWriter(filePath, append: true))
{
writer.WriteLine("March: $12,000"); // Added to existing content
}
Reading and Writing Binary Files
Binary files (e.g., images, executables, custom data formats) store raw bytes instead of text. Use FileStream with BinaryReader/BinaryWriter for this.
Binary Files with FileStream and BinaryReader/BinaryWriter
FileStream provides low-level byte access, while BinaryReader/BinaryWriter simplify reading/writing primitive types (int, float, string, etc.).
Example: Writing a Binary File
string filePath = @"C:\example\data.bin";
using (FileStream fs = new FileStream(filePath, FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(fs))
{
writer.Write(123); // int
writer.Write(3.14); // double
writer.Write("Hello Binary"); // string
writer.Write(true); // bool
}
Console.WriteLine("Binary file written.");
Example: Reading a Binary File
To read a binary file, you must know the order and types of data written to it:
using (FileStream fs = new FileStream(filePath, FileMode.Open))
using (BinaryReader reader = new BinaryReader(fs))
{
int number = reader.ReadInt32();
double pi = reader.ReadDouble();
string message = reader.ReadString();
bool flag = reader.ReadBoolean();
Console.WriteLine($"Int: {number}, Double: {pi}, String: {message}, Bool: {flag}");
}
// Output: Int: 123, Double: 3.14, String: Hello Binary, Bool: True
Handling Exceptions in File I/O
File operations are prone to errors (e.g., missing files, permission issues). Always use try-catch blocks to handle exceptions gracefully.
Common Exceptions
| Exception | Scenario |
|---|---|
FileNotFoundException | The file does not exist. |
UnauthorizedAccessException | Insufficient permissions to access the file. |
IOException | General I/O error (e.g., file in use by another process). |
DirectoryNotFoundException | The directory in the path does not exist. |
Example: Robust File Reading with Exceptions
string filePath = @"C:\example\missing.txt";
try
{
string content = File.ReadAllText(filePath);
Console.WriteLine(content);
}
catch (FileNotFoundException)
{
Console.WriteLine($"Error: File '{filePath}' not found.");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine($"Error: No permission to read '{filePath}'.");
}
catch (IOException ex)
{
Console.WriteLine($"I/O Error: {ex.Message}");
}
finally
{
Console.WriteLine("File operation completed (or failed).");
}
Best Practices for File I/O in C#
-
Use
usingStatements: Automatically dispose ofIDisposableobjects (e.g.,StreamReader,FileStream) to release resources.// Good: 'using' ensures disposal using (var reader = new StreamReader(filePath)) { /* ... */ } -
Handle Exceptions: Always catch specific exceptions (avoid bare
catchblocks) to provide meaningful error messages. -
Validate Paths: Check if a file/directory exists with
File.Exists(path)orDirectory.Exists(path)before operations.if (File.Exists(filePath)) { /* Read file */ } -
Avoid Hard-Coded Paths: Use
Path.Combinefor cross-platform path construction (handles slashes automatically).string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "data.txt"); -
Specify Encoding: Explicitly set encoding (e.g.,
Encoding.UTF8) when reading/writing text files to avoid issues with non-ASCII characters. -
Use Efficient Methods for Large Files: Prefer
StreamReader/StreamWriteroverFile.ReadAllTextfor files larger than 100MB to avoid high memory usage.
Conclusion
File I/O is a critical skill in C#, and the .NET framework simplifies it with intuitive classes like File, StreamReader, and BinaryWriter. Whether you’re working with text logs, binary data, or large files, understanding these tools will help you write efficient, robust code.
Start small: experiment with reading a text file, writing a log, or creating a binary data structure. With practice, you’ll master file I/O in no time!