Text File Reading

Vaibhav • September 10, 2025

Reading text files is one of the most common tasks in software development. Whether you're importing configuration settings, processing logs, or analyzing user-generated content, understanding how to read text files efficiently and safely is essential. In this article, we’ll explore the different ways to read text files in C#, how to handle large files, and how to avoid common pitfalls - all while staying within the scope of what we've covered so far in the curriculum.

Reading a Whole File at Once

The simplest way to read a text file is to use File.ReadAllText. This method reads the entire contents of a file into a single string.

string content = File.ReadAllText("notes.txt");
Console.WriteLine(content);

This is ideal for small files where you want to process or display the entire content at once. The method automatically opens, reads, and closes the file, so you don’t need to manage resources manually.

ReadAllText loads the entire file into memory. For large files, this can cause performance issues or even out-of-memory errors.

Reading Line by Line

For larger files or when you want to process each line individually, use File.ReadLines or File.ReadAllLines. These methods return an enumerable collection of strings - one for each line.

foreach (string line in File.ReadLines("log.txt"))
{
    Console.WriteLine(line);
}

ReadLines is more memory-efficient than ReadAllLines because it uses lazy evaluation - it reads lines one at a time as you iterate. This is especially useful for processing logs, CSVs, or any file where you don’t need the entire content at once.

Prefer ReadLines over ReadAllLines for large files. It reduces memory usage and improves performance.

Using StreamReader for More Control

If you need more control over how the file is read - such as reading character-by-character, skipping lines, or handling encoding - use StreamReader. This class gives you fine-grained access to the file stream.

using StreamReader reader = new StreamReader("data.txt");

while (!reader.EndOfStream)
{
    string line = reader.ReadLine();
    Console.WriteLine(line);
}

This approach is useful when you need to process the file in a custom way. StreamReader also allows you to specify the encoding, which is important when working with non-ASCII text.

StreamReader implements IDisposable, so it should be used with a using statement to ensure the file is closed properly.

Reading with Encoding

By default, StreamReader uses UTF-8 encoding. If your file uses a different encoding (e.g., UTF-16, ISO-8859-1), you can specify it explicitly:

using StreamReader reader = new StreamReader("legacy.txt", Encoding.GetEncoding("ISO-8859-1"));

string content = reader.ReadToEnd();
Console.WriteLine(content);

This ensures that special characters are interpreted correctly. Misinterpreting encoding can lead to corrupted or unreadable text.

You can detect a file’s encoding using StreamReader.CurrentEncoding after reading the first few bytes.

Handling File Not Found and Access Errors

File reading can fail for many reasons - the file might not exist, you might not have permission, or the file might be locked by another process. Always wrap file operations in a try-catch block:

try
{
    string content = File.ReadAllText("missing.txt");
}
catch (FileNotFoundException)
{
    Console.WriteLine("File not found.");
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("Access denied.");
}

This makes your application more robust and user-friendly. Instead of crashing, it can show a helpful message or take corrective action.

Reading Partial Content

Sometimes you only need part of a file - the first few lines, or a specific section. You can use StreamReader to read selectively:

using StreamReader reader = new StreamReader("data.txt");

for (int i = 0; i < 5; i++)
{
    string line = reader.ReadLine();
    Console.WriteLine(line);
}

This reads only the first five lines. You can also skip lines or search for specific markers in the file.

Reading Character by Character

For advanced scenarios like parsing custom formats or building a lexer, you might need to read one character at a time:

using StreamReader reader = new StreamReader("input.txt");

int ch;
while ((ch = reader.Read()) != -1)
{
    Console.Write((char)ch);
}

This reads the file character-by-character until the end. It’s slower than line-based reading but gives you full control over parsing.

Skipping Blank Lines and Comments

When processing configuration files or scripts, you may want to skip empty lines or lines that start with a comment symbol:

foreach (string line in File.ReadLines("config.txt"))
{
    if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#"))
        continue;

    Console.WriteLine($"Processing: {line}");
}

This pattern is common in parsers and preprocessors. It ensures that only meaningful lines are processed.

Reading Structured Text (Preview)

While this article focuses on plain text, many files use structured formats like CSV, JSON, or XML. You can still read them as text, but parsing them requires additional logic. For example, reading a CSV file line-by-line:

foreach (string line in File.ReadLines("data.csv"))
{
    string[] fields = line.Split(',');
    Console.WriteLine($"Name: {fields[0]}, Age: {fields[1]}");
}

This splits each line into fields using a comma delimiter. We’ll cover CSV processing in detail in a later article.

Performance Considerations

Reading files is generally fast, but performance can degrade with large files or inefficient patterns. Here are a few tips:

  • Use ReadLines for large files to avoid loading everything into memory.
  • Avoid repeated ReadAllText calls - cache the content if needed.
  • Use buffered reading (StreamReader) for custom parsing.

Profile your file reading logic if performance matters. Use Stopwatch to measure read times and optimize accordingly.

Summary

Reading text files in C# is straightforward but powerful. You’ve learned how to read entire files, process them line-by-line, use StreamReader for advanced control, handle encoding, and manage errors gracefully. These skills are foundational for many real-world applications - from log analysis to configuration loading. In the next article, we’ll explore how to write text files, including appending, overwriting, and formatting output for readability and structure.