ZetCode

C# StreamReader

last modified April 20, 2025

This tutorial explains how to use the StreamReader class in C# to read text data from files. StreamReader provides methods for reading character data from streams with various encodings.

The StreamReader class reads characters from a byte stream in a specific encoding. It inherits from TextReader and is designed for reading text files efficiently.

StreamReader automatically handles different text encodings like UTF-8, Unicode, and ASCII. It provides methods for reading lines, entire files, or character-by-character processing.

Basic StreamReader Example

This example demonstrates reading a text file line by line using StreamReader. It shows the simplest way to process a text file.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string filePath = "example.txt";
        
        // Create a sample file first
        File.WriteAllText(filePath, "First line\nSecond line\nThird line");

        // Read the file line by line
        using (StreamReader reader = new StreamReader(filePath))
        {
            string line;
            int lineNumber = 1;
            
            while ((line = reader.ReadLine()) != null)
            {
                Console.WriteLine($"Line {lineNumber}: {line}");
                lineNumber++;
            }
        }
    }
}

The program first creates a sample text file with three lines. Then it uses StreamReader to read the file line by line. The ReadLine method returns null when the end of file is reached.

The using statement ensures proper resource cleanup. Each line is printed with its line number. This pattern is common for text file processing in C#.

Reading Entire File at Once

StreamReader can read an entire file in one operation using ReadToEnd. This is useful for small files that can fit in memory.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string filePath = "document.txt";
        
        // Create sample file
        File.WriteAllText(filePath, "This is the entire content\nof the text file.");

        // Read entire file
        using (StreamReader reader = new StreamReader(filePath))
        {
            string content = reader.ReadToEnd();
            Console.WriteLine("File content:");
            Console.WriteLine(content);
            
            Console.WriteLine($"\nTotal characters: {content.Length}");
        }
    }
}

ReadToEnd reads all characters from the current position to the end of the stream. It returns a single string containing the entire file content.

This approach is simple but should be used with caution for large files. For big files, line-by-line reading is more memory efficient.

Reading Character by Character

StreamReader provides Read method for reading individual characters. This example shows how to process a file character by character.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string filePath = "chars.txt";
        File.WriteAllText(filePath, "ABC\n123");

        using (StreamReader reader = new StreamReader(filePath))
        {
            int ch;
            int count = 0;
            
            while ((ch = reader.Read()) != -1)
            {
                count++;
                Console.WriteLine($"Char {count}: {(char)ch} (Unicode: {ch})");
            }
            
            Console.WriteLine($"\nTotal characters read: {count}");
        }
    }
}

The Read method returns the next character as an integer, or -1 at end of file. We cast the integer to char for display.

This approach is useful for low-level text processing where you need to examine each character individually. Note it includes newline characters.

Specifying File Encoding

StreamReader can handle different text encodings. This example shows how to read files with specific encodings like UTF-8 and Unicode.

Program.cs
using System;
using System.IO;
using System.Text;

class Program
{
    static void Main()
    {
        string filePath = "unicode.txt";
        
        // Create file with Unicode encoding
        File.WriteAllText(filePath, "Привет мир!", Encoding.Unicode);

        // Read with explicit encoding
        using (StreamReader reader = new StreamReader(filePath, Encoding.Unicode))
        {
            string content = reader.ReadToEnd();
            Console.WriteLine("Read content: " + content);
            
            Console.WriteLine("\nFirst character: " + content[0]);
            Console.WriteLine("Encoding: " + reader.CurrentEncoding.EncodingName);
        }
    }
}

The example writes text using Unicode encoding and reads it back with the same encoding. CurrentEncoding shows the encoding being used.

Always specify the correct encoding when reading non-ASCII text files. The default encoding is UTF-8, but many legacy systems use other encodings.

Peeking at Next Character

StreamReader's Peek method allows looking at the next character without consuming it. This is useful for parsing scenarios.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string filePath = "data.txt";
        File.WriteAllText(filePath, "ABC\n123");

        using (StreamReader reader = new StreamReader(filePath))
        {
            Console.WriteLine("Peek first: " + (char)reader.Peek());
            Console.WriteLine("Read first: " + (char)reader.Read());
            
            Console.WriteLine("Peek again: " + (char)reader.Peek());
            Console.WriteLine("Read again: " + (char)reader.Read());
        }
    }
}

Peek returns the next character without advancing the position. It returns -1 at end of file, just like Read.

This method is particularly useful when you need to look ahead in the stream to decide how to process the next characters, such as in parsers.

Reading with Buffer

For performance-critical applications, StreamReader can read blocks of characters into a buffer. This example shows buffer-based reading.

Program.cs
using System;
using System.IO;
using System.Text;

class Program
{
    static void Main()
    {
        string filePath = "largefile.txt";
        
        // Create a large sample file
        var sb = new StringBuilder();
        for (int i = 0; i < 1000; i++)
        {
            sb.AppendLine($"Line {i + 1}");
        }
        File.WriteAllText(filePath, sb.ToString());

        // Read with buffer
        char[] buffer = new char[128];
        using (StreamReader reader = new StreamReader(filePath))
        {
            int charsRead;
            int totalChars = 0;
            
            while ((charsRead = reader.Read(buffer, 0, buffer.Length)) > 0)
            {
                totalChars += charsRead;
                Console.Write($"Read {charsRead} chars: ");
                Console.WriteLine(new string(buffer, 0, Math.Min(20, charsRead)) + "...");
            }
            
            Console.WriteLine($"\nTotal characters read: {totalChars}");
        }
    }
}

The Read overload fills a character buffer with data from the stream. It returns the number of characters actually read.

Buffer reading is more efficient than character-by-character processing for large files. The buffer size can be tuned for optimal performance.

Reading from MemoryStream

StreamReader can read from any Stream, including MemoryStream. This example shows reading text data from memory.

Program.cs
using System;
using System.IO;
using System.Text;

class Program
{
    static void Main()
    {
        // Create text in memory
        string text = "This text is in memory\nSecond line\nThird line";
        byte[] bytes = Encoding.UTF8.GetBytes(text);
        
        // Read from MemoryStream
        using (var memoryStream = new MemoryStream(bytes))
        using (var reader = new StreamReader(memoryStream))
        {
            Console.WriteLine("Reading from MemoryStream:");
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                Console.WriteLine("> " + line);
            }
        }
    }
}

The example converts a string to bytes, creates a MemoryStream from those bytes, then reads it with StreamReader. This pattern is useful for processing text data received from network or other sources.

StreamReader works the same way with MemoryStream as with file streams, demonstrating its flexibility with different stream sources.

Source

StreamReader Class Documentation

This tutorial covered reading text data in C# with StreamReader, including line-by-line reading, encodings, buffering, and memory streams. StreamReader is versatile for all text processing needs.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.