ZetCode

C# StreamWriter

last modified April 20, 2025

This tutorial explains how to use the StreamWriter class in C# to write text data to files. StreamWriter provides methods for writing text to streams with various encodings.

The StreamWriter class implements a TextWriter for writing characters to a stream in a particular encoding. It's commonly used for writing text files.

StreamWriter handles text encoding automatically and provides efficient buffered writing. It's often paired with StreamReader for reading text files.

Basic StreamWriter Example

This example demonstrates writing text to a file using StreamWriter. It shows the simplest way to create and write to a text file.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        // Create a file and write text to it
        using (StreamWriter writer = new StreamWriter("example.txt"))
        {
            writer.WriteLine("This is the first line.");
            writer.WriteLine("This is the second line.");
            writer.Write("This is ");
            writer.Write("a single line.");
        }

        Console.WriteLine("Text written to file successfully.");
    }
}

The example creates a new file named "example.txt" and writes three lines of text. The using statement ensures proper resource cleanup. WriteLine adds a newline after each string, while Write does not.

The file is automatically closed when the using block ends. This is the recommended way to work with file streams as it handles disposal automatically, even if an exception occurs.

Appending to an Existing File

StreamWriter can append text to existing files instead of overwriting them. This example shows how to add content to an existing file.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        // Append text to an existing file
        using (StreamWriter writer = new StreamWriter("log.txt", true))
        {
            writer.WriteLine($"Log entry: {DateTime.Now}");
            writer.WriteLine("Application started successfully.");
            writer.WriteLine();
        }

        Console.WriteLine("Log entry appended to file.");
    }
}

The second parameter in the StreamWriter constructor specifies whether to append to the file. Setting it to true preserves existing content. This is particularly useful for log files where you want to maintain a history of entries.

Each run of this program will add new log entries to the file without affecting previous content. The empty WriteLine adds a blank line between entries for better readability.

Specifying File Encoding

StreamWriter allows specifying text encoding when writing files. This example demonstrates writing with UTF-8 and UTF-16 encodings.

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

class Program
{
    static void Main()
    {
        // Write with UTF-8 encoding (default)
        using (StreamWriter writer = new StreamWriter("utf8.txt", false, Encoding.UTF8))
        {
            writer.WriteLine("UTF-8 encoded text: Café");
        }

        // Write with UTF-16 encoding
        using (StreamWriter writer = new StreamWriter("utf16.txt", false, Encoding.Unicode))
        {
            writer.WriteLine("UTF-16 encoded text: Café");
        }

        Console.WriteLine("Files written with different encodings.");
    }
}

The third parameter in the StreamWriter constructor specifies the text encoding. UTF-8 is the default if no encoding is specified. Different encodings handle special characters and international text differently.

UTF-8 is more space-efficient for ASCII characters, while UTF-16 might be better for languages with non-Latin characters. The choice depends on your specific requirements and the expected content.

Writing Formatted Text

StreamWriter supports formatted writing similar to Console.WriteLine. This example shows how to write formatted strings to a file.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string product = "Coffee";
        decimal price = 4.99m;
        int quantity = 10;

        using (StreamWriter writer = new StreamWriter("receipt.txt"))
        {
            writer.WriteLine("RECEIPT");
            writer.WriteLine("-------");
            writer.WriteLine("Date: {0:d}", DateTime.Today);
            writer.WriteLine("Product: {0}", product);
            writer.WriteLine("Price: {0:C}", price);
            writer.WriteLine("Quantity: {0}", quantity);
            writer.WriteLine("Total: {0:C}", price * quantity);
        }

        Console.WriteLine("Formatted receipt written to file.");
    }
}

The example uses composite formatting with placeholders ({0}, {1}, etc.) to create a structured receipt. Format specifiers like :d for dates and :C for currency make the output more readable.

This approach is useful for generating structured documents, reports, or any output where consistent formatting is important. The same formatting options available in Console.WriteLine are supported.

Flushing and AutoFlush

StreamWriter buffers writes by default. This example demonstrates manual flushing and the AutoFlush property.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        // With default buffering
        using (StreamWriter writer = new StreamWriter("buffered.txt"))
        {
            writer.WriteLine("This may not appear immediately.");
            Console.WriteLine("Check file - content may not be there yet.");
            writer.Flush(); // Force write to disk
            Console.WriteLine("Now content should be in file.");
        }

        // With AutoFlush enabled
        using (StreamWriter writer = new StreamWriter("autoflush.txt"))
        {
            writer.AutoFlush = true;
            writer.WriteLine("This appears immediately after each write.");
            Console.WriteLine("Content should be in file already.");
        }
    }
}

Flush forces any buffered data to be written to the file. AutoFlush = true makes this happen automatically after each write. Buffering improves performance by reducing disk I/O operations.

Manual flushing is useful when you need to ensure data is written at specific points. AutoFlush is convenient but may impact performance with many small writes. Choose the appropriate approach based on your needs.

Writing to MemoryStream

StreamWriter can write to any Stream, including MemoryStream. This example shows writing text to memory instead of a file.

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

class Program
{
    static void Main()
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            using (StreamWriter writer = new StreamWriter(memoryStream, Encoding.UTF8, 1024, true))
            {
                writer.WriteLine("This text is written to memory.");
                writer.WriteLine("Current time: {0:T}", DateTime.Now);
            }

            // Read back from the MemoryStream
            memoryStream.Position = 0;
            using (StreamReader reader = new StreamReader(memoryStream))
            {
                string content = reader.ReadToEnd();
                Console.WriteLine("Memory content:");
                Console.WriteLine(content);
            }
        }
    }
}

The example writes text to a MemoryStream, then reads it back. The StreamWriter constructor parameters control encoding, buffer size, and leave-open behavior. This technique is useful for in-memory text processing.

MemoryStreams are often used in unit testing, network communication, or when you need to process text without creating physical files. The Position = 0 reset is necessary before reading back the content.

Writing Asynchronously

StreamWriter supports asynchronous operations for better performance in I/O-bound applications. This example shows async file writing.

Program.cs
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        try
        {
            using (StreamWriter writer = new StreamWriter("async.txt"))
            {
                await writer.WriteLineAsync("First line written asynchronously.");
                await writer.WriteAsync("This is ");
                await writer.WriteLineAsync("also async.");
                await writer.FlushAsync();
            }

            Console.WriteLine("Asynchronous write completed.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}

Async methods (WriteLineAsync, WriteAsync, FlushAsync) don't block the calling thread during I/O operations. This improves responsiveness in GUI applications and scalability in server applications.

The async/await pattern makes asynchronous code almost as simple as synchronous code. Always handle potential exceptions when performing file I/O operations.

Source

StreamWriter Class Documentation

This tutorial covered writing text files in C# with StreamWriter, including basic writing, appending, encoding, formatting, and async operations.

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.