ZetCode

C# TextWriter

last modified April 20, 2025

This tutorial explains how to use the TextWriter class in C# to write text data to files and streams. TextWriter is an abstract class that provides methods for writing text sequentially.

The TextWriter class serves as the base class for all classes that write text. It handles character output to streams with specific encoding. Common derived classes include StreamWriter and StringWriter.

TextWriter provides methods for writing strings, characters, and primitive data types as text. It supports asynchronous operations and can be used with various text formats and encodings.

Basic TextWriter Example

This example demonstrates writing text to a file using StreamWriter, which inherits from TextWriter. We create a file and write several lines.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        using (TextWriter writer = new StreamWriter("output.txt"))
        {
            writer.WriteLine("This is the first line.");
            writer.WriteLine("Writing numbers: {0}, {1}", 42, 3.14);
            writer.Write("This will be ");
            writer.WriteLine("on the same line.");
        }
        
        Console.WriteLine("Text written to output.txt");
    }
}

The example creates a StreamWriter instance to write to output.txt. WriteLine adds text with a newline, while Write writes without a newline. The using statement ensures proper resource cleanup.

The StreamWriter automatically handles file creation and encoding (UTF-8 by default). The example demonstrates both formatted output with placeholders and simple string writing. The file will contain three lines of text with proper line endings.

Writing to a String with StringWriter

StringWriter is another TextWriter implementation that writes to a StringBuilder. This example shows how to capture text in memory.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        using (StringWriter writer = new StringWriter())
        {
            writer.WriteLine("Current date: {0:d}", DateTime.Now);
            writer.WriteLine("Current time: {0:t}", DateTime.Now);
            writer.WriteLine("Temperature: {0}°C", 23.5);
            
            string result = writer.ToString();
            Console.WriteLine("Captured text:\n" + result);
        }
    }
}

StringWriter collects all written text in memory. The ToString method retrieves the complete string. This is useful for building strings with multiple write operations.

The example demonstrates formatted date/time output and numeric values. StringWriter is particularly valuable when you need to construct complex strings before using them, such as for logging or report generation.

Specifying Text Encoding

TextWriter allows specifying character encoding when writing files. This example shows how to write text using different encodings.

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

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

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

        // Write with ASCII encoding (characters outside ASCII will be replaced)
        using (TextWriter writer3 = new StreamWriter("ascii.txt", false, Encoding.ASCII))
        {
            writer3.WriteLine("ASCII encoded text: café");
        }
        
        Console.WriteLine("Files created with different encodings");
    }
}

Different encodings handle special characters differently. UTF-8 is the most common encoding that supports all Unicode characters. ASCII only supports basic English characters.

The example creates three files with the same text but different encodings. The café text will appear correctly in UTF-8 and Unicode files but will lose the é character in ASCII. Always consider your encoding requirements when writing text files.

Asynchronous Writing

TextWriter supports asynchronous operations. This example demonstrates writing text to a file asynchronously.

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

class Program
{
    static async Task Main()
    {
        using (TextWriter writer = new StreamWriter("async.txt"))
        {
            await writer.WriteLineAsync("First line written asynchronously");
            await writer.WriteAsync("This is ");
            await writer.WriteLineAsync("combined asynchronously");
            
            char[] chars = { 'A', 's', 'y', 'n', 'c', ' ', 'c', 'h', 'a', 'r', 's' };
            await writer.WriteAsync(chars, 0, chars.Length);
        }
        
        Console.WriteLine("Asynchronous writing completed");
    }
}

Async methods like WriteLineAsync and WriteAsync don't block the calling thread. This is important for responsive applications performing file I/O.

The example shows three ways to write asynchronously: writing complete lines, writing partial strings, and writing character arrays. The async methods follow the same pattern as their synchronous counterparts but return Tasks.

Writing Formatted Data

TextWriter provides methods for writing formatted data. This example shows various formatting options.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        using (TextWriter writer = new StreamWriter("formatted.txt"))
        {
            // Basic string formatting
            writer.WriteLine("Formatted values: {0}, {1}, {2}", 10, 20.5, "text");
            
            // Number formatting
            writer.WriteLine("Currency: {0:C}", 125.8);
            writer.WriteLine("Decimal: {0:D5}", 42);
            writer.WriteLine("Fixed point: {0:F2}", 3.14159);
            
            // Date formatting
            DateTime now = DateTime.Now;
            writer.WriteLine("Short date: {0:d}", now);
            writer.WriteLine("Long date: {0:D}", now);
            writer.WriteLine("Custom format: {0:yyyy-MM-dd HH:mm:ss}", now);
        }
        
        Console.WriteLine("Formatted data written to file");
    }
}

The WriteLine method supports composite formatting with placeholders. Format strings after the colon specify how values should be displayed.

The example demonstrates numeric formatting (currency, decimal, fixed-point) and date/time formatting (short, long, custom). Format strings provide precise control over text output appearance. This is especially useful for generating reports or structured output files.

Writing to Multiple Writers

You can write to multiple TextWriter instances simultaneously. This example shows how to write to both console and file at once.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        using (TextWriter fileWriter = new StreamWriter("multioutput.txt"))
        using (TextWriter consoleWriter = Console.Out)
        {
            // Create a writer that writes to both destinations
            using (TextWriter multiWriter = TextWriter.Synchronized(
                new MultiTextWriter(fileWriter, consoleWriter)))
            {
                multiWriter.WriteLine("This will appear in both places");
                multiWriter.WriteLine("Current time: {0:T}", DateTime.Now);
                multiWriter.WriteLine("Value: {0}", 42 * 1.5);
            }
        }
        
        Console.WriteLine("Check multioutput.txt for the same content");
    }
}

class MultiTextWriter : TextWriter
{
    private readonly TextWriter[] writers;
    
    public MultiTextWriter(params TextWriter[] writers)
    {
        this.writers = writers;
    }
    
    public override Encoding Encoding =>  Encoding.UTF8;
    
    public override void Write(char value)
    {
        foreach (var writer in writers)
            writer.Write(value);
    }
    
    public override void Write(string value)
    {
        foreach (var writer in writers)
            writer.Write(value);
    }
    
    public override void Flush()
    {
        foreach (var writer in writers)
            writer.Flush();
    }
    
    public override void Close()
    {
        foreach (var writer in writers)
            writer.Close();
    }
}

The custom MultiTextWriter class forwards all writes to multiple TextWriter instances. TextWriter.Synchronized ensures thread-safe operations.

This technique is useful for logging scenarios where you want output to go to both console and file. The example writes three lines that appear identically in both destinations. The custom writer handles all basic write operations by delegating to the contained writers.

Disposing and Flushing

Proper resource management is crucial when working with TextWriter. This example demonstrates the importance of flushing and disposing.

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        // Example 1: Forgetting to flush/dispose
        TextWriter writer1 = new StreamWriter("flush1.txt");
        writer1.Write("This might not appear in the file");
        // Missing flush/dispose - content may be lost
        
        // Example 2: Manual flush
        TextWriter writer2 = new StreamWriter("flush2.txt");
        writer2.Write("This will appear because we flush");
        writer2.Flush(); // Ensures data is written to disk
        writer2.Close();
        
        // Example 3: Using statement (recommended)
        using (TextWriter writer3 = new StreamWriter("flush3.txt"))
        {
            writer3.Write("This will appear thanks to using");
        } // Automatically calls Dispose which calls Flush
        
        Console.WriteLine("Check the files to see which content appears");
    }
}

TextWriter buffers output by default. Flush writes buffered data immediately. Dispose (called by using) flushes and releases resources.

The example shows three approaches with different outcomes. The first might lose data because neither flush nor dispose is called. The second explicitly flushes and closes. The third (recommended) uses a using statement for automatic resource cleanup. Always properly manage writer resources to prevent data loss.

Source

TextWriter Class Documentation

This tutorial covered writing text data in C# with TextWriter, including basic operations, encodings, formatting, and resource management.

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.