File I/O
Definition
File I/O (Input/Output) in C# refers to reading from and writing to files and directories on the file system. .NET provides multiple layers of abstraction: low-level stream classes (FileStream, StreamReader, StreamWriter), high-level static helpers (File, Directory), and informative wrapper classes (FileInfo, DirectoryInfo). All file I/O APIs live in the System.IO namespace.
using System.IO;
// High-level — one-liners for simple operations
string text = File.ReadAllText("data.txt");
File.WriteAllText("output.txt", "Hello, World!");
// Stream-based — fine-grained control for large files
using var writer = new StreamWriter("log.txt", append: true);
writer.WriteLine("Application started");
Core Concepts
File Class
System.IO.File is a static class providing one-shot methods for common file operations. Each method opens and closes the file internally — ideal for small files.
// Reading
string text = File.ReadAllText("data.txt"); // Entire file as string
string[] lines = File.ReadAllLines("data.txt"); // Line-by-line array
byte[] bytes = File.ReadAllBytes("image.png"); // Raw bytes
// Writing
File.WriteAllText("output.txt", "Hello, World!"); // Overwrite with string
File.WriteAllLines("output.txt", new[] { "Line 1", "Line 2" }); // Overwrite with lines
File.WriteAllBytes("binary.dat", new byte[] { 0x01, 0x02 }); // Write raw bytes
// Appending
File.AppendAllText("log.txt", "New entry\n");
File.AppendAllLines("log.txt", new[] { "Entry 1", "Entry 2" });
// File information
bool exists = File.Exists("data.txt");
DateTime modified = File.GetLastWriteTime("data.txt");
long size = new FileInfo("data.txt").Length;
// Copy, Move, Delete
File.Copy("source.txt", "dest.txt", overwrite: true);
File.Move("old.txt", "new.txt");
File.Delete("temp.txt");
File.ReadAllText / WriteAllText are perfect for small files. For large files, use StreamReader/StreamWriter to avoid loading the entire content into memory at once.
StreamReader and StreamWriter
Stream-based classes for line-by-line or character-level reading and writing. They handle encoding automatically and are ideal for large or ongoing file operations.
StreamWriter
// Write to file (overwrites by default)
using (var writer = new StreamWriter("output.txt"))
{
writer.WriteLine("First line");
writer.WriteLine("Second line");
writer.Write("No newline at end");
}
// Append to existing file
using (var writer = new StreamWriter("log.txt", append: true))
{
writer.WriteLine($"[{DateTime.UtcNow:O}] Application started");
}
// With specific encoding
using var writer = new StreamWriter("output.txt", append: false, Encoding.UTF8);
writer.WriteLine("Unicode content: café, naïve");
StreamReader
// Read entire file
using (var reader = new StreamReader("data.txt"))
{
string content = reader.ReadToEnd();
}
// Read line by line
using (var reader = new StreamReader("data.txt"))
{
string? line;
while ((line = reader.ReadLine()) is not null)
{
Console.WriteLine(line);
}
}
// Read character by character
using (var reader = new StreamReader("data.txt"))
{
int ch;
while ((ch = reader.Read()) != -1)
{
Console.Write((char)ch);
}
}
using with streamsStreams wrap unmanaged file handles. The using statement (or using declaration) ensures the handle is released immediately when you are done, even if an exception occurs. Without it, the handle stays open until the garbage collector runs, which can cause file locking issues.