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.
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.
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.
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.
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.
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.
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.
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
List all C# tutorials.