C# write text files
last modified October 18, 2026
In this article we show how to write text files in C#. Input and output operations in C# are 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# stream
Stream provides a generic interface to the types of input and output,
and isolates the programmer from the specific details of the operating system and
the underlying devices. For instance, MemoryStream works with data
located in memory and FileStream with data in files.
There are several levels at which we can write text:
- the static
Filemethods, such asFile.WriteAllText, which do everything in one call StreamWriter, which writes text incrementally and handles the encodingFileStream, which writes raw bytes
The file examples here run in the current directory. Relative paths are resolved against the current working directory, not the location of the program.
C# write text file with File.WriteAllText
The File.WriteAllText method creates a new file, writes the contents
to the file, and then closes the file. If the target file already exists, it is
overwritten.
var path = "data.txt";
string text = "old falcon";
File.WriteAllText(path, text);
Console.WriteLine("text written");
Console.WriteLine(File.ReadAllText(path));
// writing again replaces the content
File.WriteAllText(path, "new content");
Console.WriteLine(File.ReadAllText(path));
The example writes text to a file and reads it back.
File.WriteAllText(path, text);
The File.WriteAllText method takes the file to write to and the
string to write. An optional third parameter is the encoding; if it is not
specified, UTF-8 without a byte order mark is used.
$ dotnet run text written old falcon new content
The second call replaced the earlier content.
C# write text with File.WriteAllLines
The File.WriteAllLines method creates a new file, writes a collection
of strings to the file, one per line, and then closes the file.
var path = "data.txt";
string[] lines = ["old falcon", "deep forest", "golden ring"];
File.WriteAllLines(path, lines);
Console.WriteLine("lines written to file");
// any IEnumerable<string> works, so lines can be produced lazily
File.WriteAllLines("squares.txt", Enumerable.Range(1, 5).Select(i => $"{i} squared is {i * i}"));
Console.WriteLine(File.ReadAllText("squares.txt"));
The example writes three lines to a file and then a computed sequence of lines to another.
string[] lines = ["old falcon", "deep forest", "golden ring"]; File.WriteAllLines(path, lines);
This is a string array of three lines, created with a collection expression. Each element is followed by the line terminator of the platform.
File.WriteAllLines("squares.txt", Enumerable.Range(1, 5).Select(i => $"{i} squared is {i * i}"));
The method accepts any IEnumerable<string>. The lines are
produced and written one by one, so a huge sequence is never held in memory.
$ dotnet run lines written to file 1 squared is 1 2 squared is 4 3 squared is 9 4 squared is 16 5 squared is 25
C# append text to a file
The File.AppendAllText and File.AppendAllLines methods
add text to the end of a file. If the file does not exist, it is created.
var path = "log.txt"; File.WriteAllText(path, "start" + Environment.NewLine); File.AppendAllText(path, "second line" + Environment.NewLine); File.AppendAllLines(path, ["third line", "fourth line"]); Console.WriteLine(File.ReadAllText(path));
The example starts a file and appends to it.
File.WriteAllText(path, "start" + Environment.NewLine); File.AppendAllText(path, "second line" + Environment.NewLine);
AppendAllText adds exactly the given string, so we must add the line
terminator ourselves. Environment.NewLine is \n on Linux
and macOS and \r\n on Windows.
$ dotnet run start second line third line fourth line
C# write text with File.WriteAllBytes
The File.WriteAllBytes method creates a new file, writes the
specified byte array to the file, and then closes the file. If the target file
already exists, it is overwritten.
using System.Text;
var path = "data.txt";
string text = "Today is a beautiful day. We go swimming and fishing.";
byte[] data = Encoding.UTF8.GetBytes(text);
File.WriteAllBytes(path, data);
Console.WriteLine("data written to file");
Console.WriteLine(new FileInfo(path).Length);
// the encoding matters for non-ASCII text
string accented = "žltý kôň";
Console.WriteLine(Encoding.ASCII.GetBytes(accented).Length);
Console.WriteLine(Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(accented)));
Console.WriteLine(Encoding.UTF8.GetBytes(accented).Length);
The example writes text into a file as bytes.
byte[] data = Encoding.UTF8.GetBytes(text); File.WriteAllBytes(path, data);
First, we transform the text into bytes with the Encoding.UTF8.GetBytes
method. The array of bytes is then written with File.WriteAllBytes.
Working with bytes makes us responsible for the encoding.
Console.WriteLine(Encoding.ASCII.GetBytes(accented).Length); Console.WriteLine(Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(accented))); Console.WriteLine(Encoding.UTF8.GetBytes(accented).Length);
ASCII supports only 128 characters. The accented letters are replaced by question marks, and the original text is lost. UTF-8 stores them in two bytes each, so the 8 characters need 12 bytes. Use UTF-8 unless a format demands otherwise.
$ dotnet run data written to file 53 8 ?lt? k?? 12
C# write text with StreamWriter
The StreamWriter writes text to a stream in a given encoding. Its
WriteLine method writes a string followed by a line terminator, and
Write writes without one. Visit the
C# StreamWriter tutorial for details.
var path = "data.txt";
using var sw = new StreamWriter(path);
sw.WriteLine("old falcon");
sw.WriteLine($"{3} hawks, {2.5:F2} owls");
sw.Write("no newline here");
Console.WriteLine("data written to file");
// File.CreateText returns a StreamWriter as well
using (StreamWriter w = File.CreateText("other.txt"))
{
w.WriteLine("created with File.CreateText");
}
The example writes lines to a text file.
using var sw = new StreamWriter(path);
sw.WriteLine("old falcon");
We create a writer and write a line. The using declaration releases
the file when the sw variable goes out of scope, which also flushes
the internal buffer. Without it, the data might never reach the disk.
using (StreamWriter w = File.CreateText("other.txt"))
File.CreateText is a shortcut that creates (or truncates) a file and
returns a StreamWriter for it.
$ dotnet run data written to file $ cat data.txt old falcon 3 hawks, 2.50 owls no newline here
C# write text with FileStream
The Write method of FileStream writes a block of bytes
to the file stream.
public override void Write(byte[] array, int offset, int count);
The first parameter is the buffer containing the data to write. The second is the
zero-based offset in the array from which to begin copying bytes. The third is the
number of bytes to write. There is also an overload that takes a
ReadOnlySpan<byte>, which we use in the next example.
using System.Text;
var path = "data.txt";
File.WriteAllText(path, "0123456789 this is long old content");
// OpenWrite does not truncate the file
using (FileStream fs = File.OpenWrite(path))
{
fs.Write(Encoding.UTF8.GetBytes("falcon\nhawk"));
}
Console.WriteLine(File.ReadAllText(path));
Console.WriteLine("--");
// File.Create (or FileMode.Create) truncates
using (FileStream fs = File.Create(path))
{
fs.Write(Encoding.UTF8.GetBytes("falcon\nhawk"));
}
Console.WriteLine(File.ReadAllText(path));
Console.WriteLine("--");
using (var fs = new FileStream(path, FileMode.Append, FileAccess.Write))
{
fs.Write(Encoding.UTF8.GetBytes("\nforest"));
}
Console.WriteLine(File.ReadAllText(path));
The example shows three ways of opening the file stream.
using (FileStream fs = File.OpenWrite(path))
{
fs.Write(Encoding.UTF8.GetBytes("falcon\nhawk"));
}
File.OpenWrite opens an existing file for writing and positions the
stream at the beginning, but it does not truncate it. When the new content
is shorter than the old one, the tail of the old content remains in the file. This
is a common source of corrupted files.
using (FileStream fs = File.Create(path))
File.Create truncates an existing file, so only the new content
remains. The equivalent is FileMode.Create or
FileMode.Truncate.
using (var fs = new FileStream(path, FileMode.Append, FileAccess.Write))
FileMode.Append positions the stream at the end of the file.
$ dotnet run falcon hawkthis is long old content -- falcon hawk -- falcon hawk forest
C# write text asynchronously
In the previous examples, we have written text synchronously. C# also provides equivalent asynchronous methods, which free the calling thread while the operating system performs the I/O. This matters most in applications that serve many requests or have a user interface.
var path = "data.txt";
await File.WriteAllTextAsync(path, "an old falcon");
await File.WriteAllLinesAsync("lines.txt", ["one", "two", "three"]);
await File.AppendAllTextAsync(path, "\nand a hawk");
Console.WriteLine(await File.ReadAllTextAsync(path));
using var cts = new CancellationTokenSource();
await File.WriteAllTextAsync("c.txt", "with a token", cts.Token);
await using var sw = new StreamWriter("big.txt");
for (int i = 0; i < 1000; i++)
{
await sw.WriteLineAsync($"line {i}");
}
Console.WriteLine("done");
In the example, we write to text files asynchronously.
await File.WriteAllTextAsync(path, "an old falcon");
await File.WriteAllLinesAsync("lines.txt", ["one", "two", "three"]);
await File.AppendAllTextAsync(path, "\nand a hawk");
The await operator suspends the method until the awaited task
finishes, without blocking a thread. The asynchronous methods accept a
CancellationToken as the last argument.
await using var sw = new StreamWriter("big.txt");
For incremental writing we use await using with
WriteLineAsync, so even the final flush is asynchronous.
$ dotnet run an old falcon and a hawk done
C# write text with an encoding
The default encoding of the .NET write methods is UTF-8 without a byte order mark (BOM). We can request another encoding or a BOM, for example for programs that need it to recognize UTF-8.
using System.Text;
// UTF-8 without a BOM is the default; ask for a BOM or another encoding explicitly
File.WriteAllText("plain.txt", "café");
File.WriteAllText("bom.txt", "café", new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
File.WriteAllText("utf16.txt", "café", Encoding.Unicode);
File.WriteAllText("latin1.txt", "café", Encoding.Latin1);
foreach (var f in new[] { "plain.txt", "bom.txt", "utf16.txt", "latin1.txt" })
{
Console.WriteLine($"{f,-11} {BitConverter.ToString(File.ReadAllBytes(f))}");
}
The program writes the same word in four encodings and shows the bytes.
$ dotnet run plain.txt 63-61-66-C3-A9 bom.txt EF-BB-BF-63-61-66-C3-A9 utf16.txt FF-FE-63-00-61-00-66-00-E9-00 latin1.txt 63-61-66-E9
The "é" is two bytes in UTF-8, two in UTF-16 (where every character takes two
bytes), and one in Latin-1. EF BB BF and FF FE are the
byte order marks of UTF-8 and little-endian UTF-16. The reading methods detect a
BOM automatically.
C# write a file safely
If a program crashes, or the disk fills up, in the middle of
File.WriteAllText, the target file can be left truncated. For
important files, write to a temporary file first and then replace the original in
one step. We also create the target directory if it does not exist, because the
write methods do not.
// write to a temporary file first, then replace the target in one step,
// so a crash in the middle never leaves a half-written file
string path = "settings.txt";
File.WriteAllText(path, "old settings");
string tmp = path + ".tmp";
File.WriteAllText(tmp, "new settings");
File.Move(tmp, path, overwrite: true);
Console.WriteLine(File.ReadAllText(path));
Console.WriteLine(File.Exists(tmp));
// create the directory before writing into it
string dir = Path.Combine("output", "reports");
Directory.CreateDirectory(dir); // does nothing if it already exists
File.WriteAllText(Path.Combine(dir, "report.txt"), "report");
Console.WriteLine(Path.Combine(dir, "report.txt"));
The program replaces a settings file via a temporary file and writes a file in a new directory.
File.Move(tmp, path, overwrite: true);
The File.Move overload with the overwrite parameter
replaces the destination. On the same volume, the replacement is a rename, which is
atomic on most file systems. For Windows-specific needs there is also
File.Replace.
Directory.CreateDirectory(dir);
Directory.CreateDirectory creates all directories along the path, and
does nothing if they already exist. Path.Combine joins path segments
with the right separator for the platform.
$ dotnet run new settings False output/reports/report.txt
Source
In this article we have written to text files in various ways in C#.
Author
List all C# tutorials.