iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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 for big ones, and System.IO.Pipelines when you need maximum throughput.

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…