Skip to main content

C# StreamReader and StreamWriter

Introduction

File handling is an essential skill in programming. In C#, the StreamReader and StreamWriter classes provide a convenient way to read from and write to text files. These classes are part of the System.IO namespace and offer efficient methods for handling text-based data.

In this tutorial, you'll learn:

  • What StreamReader and StreamWriter are
  • How to read text from files line by line or all at once
  • How to write text to files
  • Best practices for file handling
  • Real-world applications

Understanding StreamReader and StreamWriter

What are Streams?

In C#, a stream is an abstraction that represents a flow of data between a source and a destination. Streams are used to read from and write to various data sources such as files, memory, and network connections.

StreamReader

StreamReader is a class that reads characters from a byte stream in a particular encoding. It's specifically designed for reading characters from files, making it ideal for text file operations.

StreamWriter

StreamWriter is a class that writes characters to a file in a specific encoding. It's used to create new text files or append to existing ones.

Reading Text Files with StreamReader

Basic Usage

To read from a text file using StreamReader, follow these steps:

csharp
using System;
using System.IO;

class Program
{
static void Main()
{
string filePath = "example.txt";

try
{
// Create an instance of StreamReader to read from a file
using (StreamReader reader = new StreamReader(filePath))
{
// Read the entire file content at once
string content = reader.ReadToEnd();
Console.WriteLine("File content:");
Console.WriteLine(content);
} // The StreamReader is automatically closed when the using block ends
}
catch (FileNotFoundException)
{
Console.WriteLine($"File not found: {filePath}");
}
catch (IOException ex)
{
Console.WriteLine($"An error occurred while reading the file: {ex.Message}");
}
}
}

Let's assume the file "example.txt" contains:

Hello, world!
This is a sample text file.
Welcome to C# file handling.

The output would be:

File content:
Hello, world!
This is a sample text file.
Welcome to C# file handling.

Reading Line by Line

Often, you'll want to process a file line by line, especially for large files:

csharp
using System;
using System.IO;

class Program
{
static void Main()
{
string filePath = "example.txt";

try
{
using (StreamReader reader = new StreamReader(filePath))
{
string line;
int lineNumber = 1;

// Read and display each line until the end of file
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine($"Line {lineNumber}: {line}");
lineNumber++;
}
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
}

Output:

Line 1: Hello, world!
Line 2: This is a sample text file.
Line 3: Welcome to C# file handling.

Useful StreamReader Methods

Here are some commonly used methods of the StreamReader class:

  • ReadToEnd(): Reads all characters from the current position to the end of the file
  • ReadLine(): Reads a line of characters from the current stream and returns it as a string
  • Read(): Reads the next character from the input stream
  • Peek(): Returns the next available character without actually reading it

Writing Text Files with StreamWriter

Basic Usage

To write to a text file using StreamWriter:

csharp
using System;
using System.IO;

class Program
{
static void Main()
{
string filePath = "output.txt";

try
{
// Create a StreamWriter instance (this will create or overwrite the file)
using (StreamWriter writer = new StreamWriter(filePath))
{
writer.WriteLine("Hello, this is the first line.");
writer.WriteLine("Second line of text.");
writer.WriteLine("Third line with some numbers: 12345");

// Write without a new line at the end
writer.Write("This text doesn't have a newline. ");
writer.Write("And this is on the same line.");
}

Console.WriteLine($"Successfully wrote to {filePath}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
}

After running this code, the file "output.txt" will contain:

Hello, this is the first line.
Second line of text.
Third line with some numbers: 12345
This text doesn't have a newline. And this is on the same line.

Appending to Files

To add content to an existing file without overwriting its contents:

csharp
using System;
using System.IO;

class Program
{
static void Main()
{
string filePath = "output.txt";

try
{
// Pass 'true' as the second parameter to append to the file
using (StreamWriter writer = new StreamWriter(filePath, true))
{
writer.WriteLine(); // Add an empty line
writer.WriteLine("This line is appended to the existing file.");
writer.WriteLine($"Current timestamp: {DateTime.Now}");
}

Console.WriteLine($"Successfully appended to {filePath}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
}

If you run this after the previous example, "output.txt" will now contain:

Hello, this is the first line.
Second line of text.
Third line with some numbers: 12345
This text doesn't have a newline. And this is on the same line.

This line is appended to the existing file.
Current timestamp: 5/20/2023 2:45:30 PM

Best Practices for File Handling

  1. Always use using statements: This ensures that file handles are properly closed even if an exception occurs.

  2. Handle exceptions: File operations can fail for various reasons (permission issues, disk full, etc.). Always implement proper exception handling.

  3. Check if files exist before reading: Use File.Exists() to verify if a file exists before attempting to read it.

  4. Be careful with file paths: Use Path.Combine() to create file paths instead of manually concatenating strings.

  5. Consider file encoding: If working with non-English text, specify the appropriate encoding:

csharp
// Reading with UTF-8 encoding
using (StreamReader reader = new StreamReader(filePath, System.Text.Encoding.UTF8))
{
// Read operations
}

// Writing with UTF-8 encoding
using (StreamWriter writer = new StreamWriter(filePath, false, System.Text.Encoding.UTF8))
{
// Write operations
}

Practical Examples

Example 1: Creating a Simple Log File

csharp
using System;
using System.IO;

class Logger
{
private string logFilePath;

public Logger(string filePath)
{
logFilePath = filePath;
}

public void LogMessage(string message)
{
try
{
using (StreamWriter writer = new StreamWriter(logFilePath, true))
{
writer.WriteLine($"[{DateTime.Now}] {message}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Failed to write to log file: {ex.Message}");
}
}
}

class Program
{
static void Main()
{
Logger logger = new Logger("application.log");

logger.LogMessage("Application started");

// Simulate some application operations
logger.LogMessage("User login: john_doe");
logger.LogMessage("Database connection established");

// Simulate an error
try
{
int result = 10 / 0; // This will cause a division by zero exception
}
catch (Exception ex)
{
logger.LogMessage($"ERROR: {ex.Message}");
}

logger.LogMessage("Application shutdown");

Console.WriteLine("Check application.log for the log entries");
}
}

The output in "application.log" would look like:

[5/20/2023 3:01:45 PM] Application started
[5/20/2023 3:01:45 PM] User login: john_doe
[5/20/2023 3:01:45 PM] Database connection established
[5/20/2023 3:01:45 PM] ERROR: Division by zero
[5/20/2023 3:01:45 PM] Application shutdown

Example 2: CSV Data Processing

csharp
using System;
using System.IO;
using System.Collections.Generic;

class Student
{
public string Name { get; set; }
public int Age { get; set; }
public double GradeAverage { get; set; }

public override string ToString()
{
return $"{Name}, {Age}, {GradeAverage}";
}
}

class Program
{
static void Main()
{
string inputFile = "students.csv";
string outputFile = "high_performers.csv";

List<Student> students = new List<Student>();

// Read students from CSV
try
{
using (StreamReader reader = new StreamReader(inputFile))
{
// Skip header line
reader.ReadLine();

string line;
while ((line = reader.ReadLine()) != null)
{
string[] parts = line.Split(',');
if (parts.Length == 3)
{
Student student = new Student
{
Name = parts[0].Trim(),
Age = int.Parse(parts[1].Trim()),
GradeAverage = double.Parse(parts[2].Trim())
};

students.Add(student);
}
}
}

Console.WriteLine($"Read {students.Count} students from the file.");

// Filter high performing students (grade average > 85)
List<Student> highPerformers = students.FindAll(s => s.GradeAverage > 85);

// Write high performers to a new CSV file
using (StreamWriter writer = new StreamWriter(outputFile))
{
writer.WriteLine("Name,Age,GradeAverage");

foreach (Student student in highPerformers)
{
writer.WriteLine(student.ToString());
}
}

Console.WriteLine($"Wrote {highPerformers.Count} high performers to {outputFile}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
}

Let's assume that "students.csv" contains:

Name,Age,GradeAverage
John Smith,19,87.5
Mary Johnson,20,92.0
Robert Brown,18,79.3
Sarah Wilson,19,88.7
Michael Davis,21,76.2

After running the program, "high_performers.csv" would contain:

Name,Age,GradeAverage
John Smith, 19, 87.5
Mary Johnson, 20, 92
Sarah Wilson, 19, 88.7

Summary

In this tutorial, we've explored C# StreamReader and StreamWriter classes for file handling:

  • StreamReader allows you to read text from files, either all at once or line by line
  • StreamWriter enables you to write or append text to files
  • Always use using statements to ensure proper resource cleanup
  • Handle exceptions to make your code more robust
  • Consider encoding when working with international text

These classes provide efficient ways to work with text files in C#, and are essential tools for many real-world applications, from logging to data processing.

Exercises

  1. Create a program that counts the number of words, lines, and characters in a text file.

  2. Write a program that reads a CSV file containing product data (name, price, quantity) and calculates the total inventory value.

  3. Create a simple text editor that can open, edit, and save text files using StreamReader and StreamWriter.

  4. Implement a program that merges multiple text files into a single file.

  5. Create a log analysis tool that reads a log file and provides statistics about error occurrences.

Additional Resources

Happy coding!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)