Lesson 22 — Reading and Writing Files

File Handling

C# file IO with System.IO is straightforward for most tasks. The File and StreamWriter/StreamReader classes cover the common cases cleanly.

CSHARP — File class — simple API
using System.IO;

// Write all lines at once (simplest):
File.WriteAllLines("learners.txt", new[] {
    "Thandi Mokoena,Java,74.5",
    "Sipho Dlamini,Python,81.0",
    "Lerato Khumalo,SQL,68.0"
});

// Append:
File.AppendAllText("learners.txt", "Bongani Nkosi,C#,88.5\n");

// Read all lines:
string[] lines = File.ReadAllLines("learners.txt");
foreach (var line in lines)
    Console.WriteLine(line);

// Read as single string:
string content = File.ReadAllText("learners.txt");
Console.WriteLine($"Total chars: {content.Length}");

StreamWriter for Large Files

CSHARP — StreamWriter/Reader
// using block — guarantees Close() is called:
using (var writer = new StreamWriter("log.txt", append: true)) {
    writer.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] Programme started.");
    writer.WriteLine("=".PadRight(50, '='));
}

// Reading line by line (efficient for large files):
using (var reader = new StreamReader("learners.txt")) {
    string? line;
    int lineNum = 0;
    while ((line = reader.ReadLine()) != null) {
        lineNum++;
        var parts = line.Split(',');
        if (parts.Length == 3)
            Console.WriteLine($"{lineNum}. {parts[0]} | {parts[1]} | {parts[2]}%");
    }
}

File Checks and Path Handling

CSHARP — File checks
// Check before operating:
if (!File.Exists("config.txt")) {
    File.WriteAllText("config.txt", "MaxLearners=30\n");
    Console.WriteLine("Config file created.");
}

// Path handling — use Path.Combine for cross-platform paths:
string folder  = "data";
string file    = "learners.csv";
string fullPath = Path.Combine(folder, file);

Directory.CreateDirectory(folder); // create if not exists
Console.WriteLine($"Saving to: {fullPath}");

Practice Task

Your Turn

Write a LearnerRegistry: SaveToFile(List<Learner> learners, string path) writes CSV lines; LoadFromFile(string path) reads them back and returns a List. Handle FileNotFoundException, IOException, and FormatException. Test by saving 5 learners, clearing the list, reloading, and printing each.

Common Mistakes

  • Not using using blocks — files may not close if an exception occurs.
  • File.ReadAllLines on huge files — loads everything into memory. Use StreamReader for streaming.
  • Hardcoded backslash paths on Windows — use Path.Combine for cross-platform.
  • Not handling NumberFormatException when parsing numeric columns.

Professional Tip

Always handle the FileNotFoundException separately from IOException — they indicate different problems (file doesn't exist vs IO error) and need different responses.

Mini Quiz

What does File.WriteAllLines() do if the file already exists?