ZetCode

C# StreamWriter

last modified October 18, 2026

In this article we show how to write text files in C# with StreamWriter. The C# tutorial is a comprehensive tutorial on the C# language.

Input and output in C# is based on streams. A Stream is an abstract base class of all streams. A stream is an abstraction of a sequence of bytes, such as a file, an input/output device, an inter-process communication pipe, or a TCP/IP socket.

C# StreamWriter

StreamWriter writes characters to a stream in a particular encoding. It defaults to UTF-8 without a byte order mark (BOM) unless specified otherwise. It buffers its output, so data may not reach the file until the buffer is flushed or the writer is disposed. To read text files, use StreamReader.

For a simple one-shot write, the File.WriteAllText, File.WriteAllLines, and File.AppendAllText methods are shorter. StreamWriter is the better choice when we write incrementally, in a loop, or to a stream that is not a file.

C# StreamWriter example

The WriteLine method writes a string followed by a line terminator to the stream. There are many overloads of WriteLine for different types.

Program.cs
var path = "data.txt";

using var sw = new StreamWriter(path);
sw.WriteLine("old falcon");
sw.WriteLine("stormy night");

Console.WriteLine("data written to file");

The example writes two lines to the data.txt file.

using var sw = new StreamWriter(path);

A StreamWriter is created. If the file exists, it is overwritten; otherwise it is created. The using declaration disposes the writer at the end of the enclosing scope, which flushes the buffer and closes the file.

sw.WriteLine("old falcon");
sw.WriteLine("stormy night");

Two lines are written to the stream.

$ dotnet run
data written to file
$ cat data.txt
old falcon
stormy night

C# StreamWriter write formatted string

We can write formatted text with interpolated strings, or with the composite format overload of WriteLine, which works like string.Format. The Write method writes without a line terminator.

Visit the C# string format tutorial to learn more about formatting in C#.

Program.cs
int n = 3;
string word = "hawks";
double temp = 21.456;

using var sw = new StreamWriter("data.txt");

sw.WriteLine($"There are {n} {word} in the sky");
sw.WriteLine("The temperature is {0:F1} degrees", temp);
sw.Write("no newline");
sw.Write('.');
sw.WriteLine();

Console.WriteLine("data written to file");

The example writes formatted lines to the file.

sw.WriteLine($"There are {n} {word} in the sky");

An interpolated string embeds the values directly in the text.

sw.WriteLine("The temperature is {0:F1} degrees", temp);

The composite format uses indexed placeholders. The F1 format specifier rounds the number to one decimal place.

sw.Write("no newline");
sw.Write('.');
sw.WriteLine();

Write adds text to the current line; the final empty WriteLine ends it.

$ dotnet run
data written to file
$ cat data.txt
There are 3 hawks in the sky
The temperature is 21.5 degrees
no newline.

Numbers are formatted using the current culture. To get the same output on every machine, format the values with CultureInfo.InvariantCulture, for example FormattableString.Invariant($"...").

C# StreamWriter append text

We can create a StreamWriter that appends text to the end of an existing file.

public StreamWriter(string path, bool append);

If append is true and the file exists, the data is added to the end of the file. If the file does not exist, it is created. If append is false, an existing file is overwritten.

Program.cs
var path = "words.txt";

using (var sw = new StreamWriter(path, append: true))
{
    sw.WriteLine("hawk");
}

using (var sw = new StreamWriter(path, append: true))
{
    sw.WriteLine("falcon");
}

Console.WriteLine(File.ReadAllText(path));

The example appends two words to the words.txt file with two separate writers.

using (var sw = new StreamWriter(path, append: true))
{
    sw.WriteLine("hawk");
}

We open the writer in append mode. The using block ends at the closing brace, so the file is closed before we open it again.

$ dotnet run
hawk
falcon

$ dotnet run
hawk
falcon
hawk
falcon

Each run adds two more lines. The empty line at the end is the line terminator of the last WriteLine plus the one added by Console.WriteLine.

C# StreamWriter encoding

The third constructor argument selects the encoding. By default, UTF-8 is used without a byte order mark. We can ask for a BOM or choose a different encoding.

Program.cs
using System.Text;

var path = "data-latin2.txt";
var enc = Encoding.Latin1;

using (var sw = new StreamWriter(path, false, enc))
{
    sw.WriteLine("café");
}

byte[] bytes = File.ReadAllBytes(path);
Console.WriteLine(BitConverter.ToString(bytes));

using (var sw = new StreamWriter("data-utf8.txt", false, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true)))
{
    sw.WriteLine("café");
}

Console.WriteLine(BitConverter.ToString(File.ReadAllBytes("data-utf8.txt")));

We write the word "café" in two encodings and inspect the raw bytes.

using (var sw = new StreamWriter(path, false, enc))

The second argument is the append flag, the third is the encoding. Latin-1 (ISO-8859-1) stores the character "é" as the single byte E9.

new UTF8Encoding(encoderShouldEmitUTF8Identifier: true)

This UTF-8 encoding emits the BOM EF BB BF at the beginning of the file. Some Windows tools expect it; most other tools do not need it. In UTF-8, the character "é" takes two bytes, C3 A9.

$ dotnet run
63-61-66-E9-0A
EF-BB-BF-63-61-66-C3-A9-0A

C# StreamWriter async

For I/O-bound code, such as writing to slow storage or to a network stream, we can use the asynchronous methods so the calling thread is not blocked.

Program.cs
var path = "numbers.txt";

await using (var sw = new StreamWriter(path))
{
    for (int i = 1; i <= 5; i++)
    {
        await sw.WriteLineAsync($"line {i}");
    }
}

Console.WriteLine(await File.ReadAllTextAsync(path));

The example writes five lines asynchronously.

await using (var sw = new StreamWriter(path))
{
    for (int i = 1; i <= 5; i++)
    {
        await sw.WriteLineAsync($"line {i}");
    }
}

StreamWriter implements IAsyncDisposable, so we use await using; the final flush is then asynchronous as well. Mixing synchronous calls with an asynchronous disposal, or the other way around, defeats the purpose.

$ dotnet run
line 1
line 2
line 3
line 4
line 5

C# StreamWriter flush

A StreamWriter keeps characters in an internal buffer. The Flush method writes the buffer to the underlying stream. Setting AutoFlush to true flushes after every write call; this is safer for logs, but slower.

Program.cs
var path = "log.txt";

using var sw = new StreamWriter(path) { AutoFlush = true };

sw.WriteLine("first message");
Console.WriteLine($"on disk after AutoFlush: {new FileInfo(path).Length} bytes");

using var sw2 = new StreamWriter("log2.txt");
sw2.WriteLine("buffered message");
Console.WriteLine($"on disk before Flush: {new FileInfo("log2.txt").Length} bytes");
sw2.Flush();
Console.WriteLine($"on disk after Flush: {new FileInfo("log2.txt").Length} bytes");

We compare a writer with AutoFlush turned on with a buffered writer.

using var sw = new StreamWriter(path) { AutoFlush = true };

The data reaches the file immediately after each write.

sw2.WriteLine("buffered message");
Console.WriteLine($"on disk before Flush: {new FileInfo("log2.txt").Length} bytes");
sw2.Flush();

Without AutoFlush, the file is still empty until we call Flush (or dispose the writer).

$ dotnet run
on disk after AutoFlush: 14 bytes
on disk before Flush: 0 bytes
on disk after Flush: 17 bytes

The sizes include the line terminator; on Windows, which uses two characters for a newline, they would be one byte larger per line.

C# StreamWriter with FileStreamOptions

Since .NET 6, StreamWriter can be created from a FileStreamOptions object, which gives control over the file mode, access, sharing, and buffer size without creating a FileStream manually.

Program.cs
var path = "report.csv";

var cars = new (string Name, int Price)[]
{
    ("Audi", 52642), ("Mercedes", 57127), ("Skoda", 9000)
};

var opts = new FileStreamOptions
{
    Mode = FileMode.Create,
    Access = FileAccess.Write,
    Share = FileShare.Read
};

using (var sw = new StreamWriter(path, opts))
{
    sw.WriteLine("name,price");

    foreach (var (name, price) in cars)
    {
        sw.WriteLine($"{name},{price}");
    }
}

Console.WriteLine(File.ReadAllText(path));

We write a small CSV file.

var opts = new FileStreamOptions
{
    Mode = FileMode.Create,
    Access = FileAccess.Write,
    Share = FileShare.Read
};

The file is created or truncated, opened for writing, and other processes may read it while we write.

$ dotnet run
name,price
Audi,52642
Mercedes,57127
Skoda,9000

Source

StreamWriter class

In this article we have written text files in C# with StreamWriter.

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.