Files / Streams
System.IO and the modern File / Stream APIs give you everything from one-line reads to streaming gigabytes without holding them in memory. Use the high-level helpers for small files, FileStream + Memory
Read, write, stream, watch, and async patterns
EXAMPLE
using System;
using System.IO;
using System.IO.Pipelines;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class FilesDemo
{
static async Task Main()
{
// 1) One-liner helpers — fine for small files
await File.WriteAllTextAsync("hello.txt", "Hello, world\n");
Console.WriteLine(await File.ReadAllTextAsync("hello.txt"));
// Lines, bytes, JSON — same shape
string[] lines = await File.ReadAllLinesAsync("hello.txt");
byte[] bytes = await File.ReadAllBytesAsync("hello.txt");
await File.WriteAllBytesAsync("copy.bin", bytes);
// 2) Append vs overwrite
await File.AppendAllTextAsync("log.txt", $"[{DateTime.UtcNow:O}] started\n");
// 3) Stream large files — never load everything into memory
await using var src = File.OpenRead("big-input.csv");
await using var dst = File.Create("big-output.csv");
await src.CopyToAsync(dst, bufferSize: 1 << 16);
// 4) Read by line using StreamReader (good for very long files)
await using var fs = File.OpenRead("huge.log");
using var reader = new StreamReader(fs);
while (await reader.ReadLineAsync() is string line)
{
if (line.Contains("ERROR")) Console.WriteLine(line);
}
// 5) Write JSON object directly to a stream (no intermediate string)
await using var jsonStream = File.Create("order.json");
await JsonSerializer.SerializeAsync(jsonStream,
new { id = "o1", total = 49.95, currency = "AUD" },
new JsonSerializerOptions { WriteIndented = true });
// 6) Path helpers — never concatenate paths by hand
string root = Path.Combine(AppContext.BaseDirectory, "data");
Directory.CreateDirectory(root); // no-op if exists
string target = Path.Combine(root, "2026", "june", "report.csv");
Directory.CreateDirectory(Path.GetDirectoryName(target)!);
// 7) Atomic write — write to temp, then rename so crash mid-write does not corrupt
string tmp = target + ".tmp";
await File.WriteAllTextAsync(tmp, "id,total\n1,100\n");
File.Move(tmp, target, overwrite: true);
// 8) Enumerate a directory tree without loading the list into memory
foreach (var path in Directory.EnumerateFiles(root, "*.csv", SearchOption.AllDirectories))
{
Console.WriteLine(path);
}
// 9) Watch for changes — fire on create/change/delete
using var watcher = new FileSystemWatcher(root)
{
IncludeSubdirectories = true,
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite,
EnableRaisingEvents = true,
};
watcher.Changed += (_, e) => Console.WriteLine($"changed: {e.FullPath}");
watcher.Created += (_, e) => Console.WriteLine($"created: {e.FullPath}");
// 10) High-throughput parsing with Pipelines
var pipe = new Pipe();
_ = Task.Run(async () => {
await using var s = File.OpenRead("big-input.csv");
await s.CopyToAsync(pipe.Writer.AsStream());
await pipe.Writer.CompleteAsync();
});
await ConsumeAsync(pipe.Reader);
// 11) Async file I/O is cooperatively non-blocking, but disk I/O still has its own contention.
// Benchmark; sometimes synchronous + a worker thread is faster.
}
static async Task ConsumeAsync(PipeReader reader)
{
while (true)
{
var read = await reader.ReadAsync();
if (read.IsCompleted && read.Buffer.IsEmpty) break;
foreach (var segment in read.Buffer)
/* parse segment.Span here */ ;
reader.AdvanceTo(read.Buffer.End);
}
}
}
Why it matters
Always write to a temp file then File.Move to the final name when atomicity matters. A crash, power loss, or kill during a direct write to the target leaves you with a truncated file the next process reads as corrupt — the rename is the cheapest, most reliable durability primitive at the OS level.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
File.WriteAllText("out.txt", "hi");
string data = File.ReadAllText("in.txt");
using var sr = new StreamReader("big.log");
while (sr.ReadLine() is string line) Process(line);
Try it Yourself »
Discussion
Loading…