C# TextWriterとは?使い方・StreamWriterとの違い・実装例を初心者向けに解説

1. C#のTextWriterとは?初心者向けに役割をわかりやすく解説

1-1. TextWriterは文字を書き込むための抽象クラス

TextWriterは、文字や文字列を書き込むための共通機能を定義した抽象クラスです。名前空間はSystem.IOです。

抽象クラスとは、基本的な設計や機能を定義するためのクラスです。通常のクラスのように、次のようなコードで直接インスタンスを生成することはできません。

// コンパイルエラーTextWriter writer = new TextWriter();

実際に文字を書き込むときは、TextWriterを継承したクラスを使用します。

代表的な派生クラスは次の2つです。

  • StreamWriter:ファイルやストリームに文字を書き込む

  • StringWriter:メモリ上の文字列に文字を書き込む

たとえば、ファイルに文字を書き込む場合は次のようにします。

using System.IO;

using TextWriter writer = new StreamWriter("output.txt");writer.WriteLine("C# TextWriterのサンプルです。");

変数の型はTextWriterですが、実際に生成しているオブジェクトはStreamWriterです。

1-2. ファイル・文字列・ストリームへの出力を共通の書き方で扱える

TextWriterの大きな特徴は、出力先が違っても共通のメソッドで文字を書き込めることです。

たとえば、ファイルへ書き込むStreamWriterでも、メモリ上に文字列を作るStringWriterでも、次のメソッドを使用できます。

writer.Write("文字列");writer.WriteLine("改行付きの文字列");

出力先ごとに異なる書き方を覚える必要がなく、WriteWriteLineという共通の操作で扱えます。

このように、具体的な出力先を意識せず、「文字を書き込めるオブジェクト」として扱えることがTextWriterの役割です。

1-3. TextWriterが使われる主な場面

TextWriterは、主に次のような場面で使われます。

  • テキストファイルやログファイルを作成する

  • CSVや設定ファイルを出力する

  • HTMLやXMLなどの文字列を組み立てる

  • コンソール、ファイル、メモリの出力処理を共通化する

  • 単体テストで出力内容を確認する

  • 独自の文字出力クラスを実装する

単純にファイルへ書き込むだけなら、最初からStreamWriter型を使っても問題ありません。一方、出力先をあとから切り替えたい場合や、再利用しやすいメソッドを作りたい場合は、TextWriter型で扱うと便利です。

2. TextWriterの基本的な使い方

2-1. Writeメソッドで文字列を書き込む

Writeメソッドは、指定した値を書き込みます。書き込み後に自動では改行されません。

using System.IO;

using TextWriter writer = new StreamWriter("sample.txt");

writer.Write("Hello");writer.Write(" ");writer.Write("C#");

作成されるsample.txtの内容は次のとおりです。

Hello C#

文字列だけでなく、数値や真偽値なども書き込めます。

writer.Write(100);writer.Write(true);writer.Write(3.14);

これらの値は、文字列表現に変換されて出力されます。

2-2. WriteLineメソッドで改行付きの文字列を書き込む

WriteLineメソッドは、値を書き込んだあとに改行を追加します。

using System.IO;

using TextWriter writer = new StreamWriter("sample.txt");

writer.WriteLine("1行目です。");writer.WriteLine("2行目です。");writer.WriteLine("3行目です。");

出力結果は次のとおりです。

1行目です。2行目です。3行目です。

引数を指定せずに呼び出すと、改行だけを書き込めます。

writer.WriteLine("見出し");writer.WriteLine();writer.WriteLine("本文");

文章やCSV、ログなど、行単位でデータを出力するときはWriteLineが便利です。

2-3. using文でリソースを安全に解放する

StreamWriterのようにファイルを扱うオブジェクトは、使用後にリソースを解放する必要があります。

C#では、using文またはusing宣言を使うのが一般的です。

using System.IO;

using (TextWriter writer = new StreamWriter("sample.txt")){writer.WriteLine("ファイルに書き込みます。");}

新しい書き方では、次のようなusing宣言も使用できます。

using System.IO;

using TextWriter writer = new StreamWriter("sample.txt");

writer.WriteLine("ファイルに書き込みます。");

変数が有効な範囲を抜けると、自動的にDisposeが呼び出されます。処理中に例外が発生した場合でもリソースが解放されるため、手動でCloseを呼ぶより安全です。

2-4. FlushとCloseの違い

Flushは、内部バッファーに残っているデータを出力先へ書き出すためのメソッドです。

writer.WriteLine("すぐに反映したいデータ");writer.Flush();

Flushを呼び出したあとも、writerは引き続き使用できます。

writer.Flush();writer.WriteLine("さらに書き込めます。");

一方、Closeはデータを書き出したうえで、ライターが保持しているリソースを解放します。

writer.Close();

Close後に再び書き込もうとすると、ObjectDisposedExceptionなどが発生する可能性があります。

違いを整理すると、次のようになります。

メソッド主な役割呼び出し後の書き込み
Flushバッファーの内容を出力する可能
Close出力を終了してリソースを解放する不可
Disposeリソースを解放する不可

通常はusingで自動的にDisposeさせればよいため、明示的なCloseは必要ありません。

3. TextWriterでファイルに書き込む実装例

3-1. StreamWriterを使ってテキストファイルに書き込む

TextWriterでファイルへ書き込む場合、実体にはStreamWriterを使用します。

using System;using System.IO;

class Program{static void Main(){using TextWriter writer = new StreamWriter("output.txt");

    writer.WriteLine("商品名: ノートパソコン");writer.WriteLine("価格: 120000円");writer.WriteLine("在庫: あり");Console.WriteLine("ファイルへの書き込みが完了しました。");}

}

このコードを実行すると、実行ディレクトリにoutput.txtが作成されます。

同名のファイルが存在する場合、基本的には既存の内容が上書きされます。

3-2. 追記モードでファイルに書き込む

既存の内容を残したまま末尾へ追加するには、StreamWriterのコンストラクターでappendtrueにします。

using System.IO;

using TextWriter writer = new StreamWriter(path: "application.log",append: true);

writer.WriteLine($"{DateTime.Now}: アプリケーションを開始しました。");

append: trueを指定すると追記モードになります。

new StreamWriter("application.log", append: true);

falseの場合は上書きモードです。

new StreamWriter("application.log", append: false);

ログファイルや処理履歴のように、過去の内容を残したい場合は追記モードを使用します。

3-3. 文字コードを指定して書き込む

文字コードを指定する場合は、StreamWriterのコンストラクターにEncodingを渡します。

using System.IO;using System.Text;

using TextWriter writer = new StreamWriter(path: "output.txt",append: false,encoding: Encoding.UTF8);

writer.WriteLine("UTF-8で保存される日本語です。");

UTF-8のBOMを付けずに出力したい場合は、UTF8Encodingを明示的に生成できます。

using System.IO;using System.Text;

Encoding utf8WithoutBom =new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);

using TextWriter writer = new StreamWriter(path: "output.txt",append: false,encoding: utf8WithoutBom);

writer.WriteLine("BOMなしUTF-8で出力します。");

ファイルを書き込む側と読み込む側で文字コードが一致していないと、文字化けする可能性があります。連携先のシステムや使用するソフトが求める文字コードを確認しましょう。

3-4. 例外処理を入れた安全な書き込み方法

ファイル出力では、権限不足、フォルダーが存在しない、ディスクやファイルの入出力エラーなどが発生する可能性があります。

using System;using System.IO;using System.Text;

class Program{static void Main(){string path = "data/output.txt";

    try{using TextWriter writer = new StreamWriter(path,append: false,encoding: Encoding.UTF8);writer.WriteLine("安全にファイルを書き込みます。");}catch (UnauthorizedAccessException ex){Console.WriteLine($"アクセス権限がありません: {ex.Message}");}catch (DirectoryNotFoundException ex){Console.WriteLine($"保存先フォルダーがありません: {ex.Message}");}catch (IOException ex){Console.WriteLine($"入出力エラーが発生しました: {ex.Message}");}}

}

usingを使用しているため、書き込み中に例外が発生しても、生成済みのライターは適切に破棄されます。

保存先フォルダーを自動的に作成したい場合は、書き込み前にDirectory.CreateDirectoryを使用できます。

string directory = "data";string path = Path.Combine(directory, "output.txt");

Directory.CreateDirectory(directory);

using TextWriter writer = new StreamWriter(path);writer.WriteLine("フォルダーを作成してから保存します。");

4. TextWriterとStreamWriterの違い

4-1. TextWriterは抽象クラス、StreamWriterは実装クラス

TextWriterStreamWriterの最も重要な違いは、クラスの役割です。

TextWriterは、文字を書き込むための共通ルールを定義する抽象クラスです。直接インスタンスを生成することはできません。

StreamWriterは、TextWriterを継承した具体的な実装クラスです。ファイルやストリームへ実際に文字を書き込めます。

TextWriter writer = new StreamWriter("output.txt");

このコードでは、変数の型がTextWriter、オブジェクトの実体がStreamWriterです。

4-2. StreamWriterはファイルやストリームへの書き込みに使う

StreamWriterは、文字を指定したストリームへ書き込むクラスです。

ファイルパスを直接指定できます。

using StreamWriter writer = new StreamWriter("output.txt");writer.WriteLine("ファイルへ出力します。");

FileStreamMemoryStreamなどのStreamを渡すこともできます。

using FileStream stream = new FileStream("output.txt",FileMode.Create,FileAccess.Write);

using StreamWriter writer = new StreamWriter(stream);writer.WriteLine("FileStreamを経由して書き込みます。");

文字コードを使って文字をバイト列へ変換し、接続されたストリームへ書き込むのがStreamWriterの役割です。

4-3. TextWriter型で受け取るメリット

メソッドの引数をTextWriter型にすると、呼び出し側が出力先を選べます。

static void WriteMessage(TextWriter writer){writer.WriteLine("処理を開始しました。");writer.WriteLine("処理が完了しました。");}

ファイルへ出力する場合はStreamWriterを渡します。

using TextWriter writer = new StreamWriter("log.txt");WriteMessage(writer);

文字列として取得する場合はStringWriterを渡します。

using StringWriter writer = new StringWriter();

WriteMessage(writer);

string result = writer.ToString();Console.WriteLine(result);

メソッド側は、出力先がファイルなのかメモリなのかを知る必要がありません。これにより、コードの再利用性やテストのしやすさが向上します。

4-4. どちらを使えばよいかの判断基準

ファイルへ書き込むことが明確で、出力先を変更する予定がない場合は、StreamWriter型を直接使うと分かりやすくなります。

using StreamWriter writer = new StreamWriter("output.txt");

複数の出力先に対応したいメソッドの引数やフィールドでは、TextWriter型が適しています。

static void Export(TextWriter writer){// 出力先に依存しない処理}

判断基準をまとめると、次のとおりです。

使用場面適した型
ファイルやストリームへ実際に出力するStreamWriter
文字列をメモリ上に生成するStringWriter
出力先に依存しないメソッドを作るTextWriter
独自の文字出力処理を実装するTextWriterを継承したクラス

5. TextWriterとStringWriterの違い

5-1. StringWriterは文字列としてメモリ上に書き込むクラス

StringWriterは、書き込んだ内容をメモリ上の文字列として保持するクラスです。

内部ではStringBuilderを利用して文字列を蓄積します。

using System;using System.IO;

using StringWriter writer = new StringWriter();

writer.WriteLine("商品一覧");writer.WriteLine("りんご");writer.WriteLine("みかん");

string result = writer.ToString();

Console.WriteLine(result);

ファイルは作成されず、ToStringメソッドで完成した文字列を取得できます。

5-2. ファイル出力ならStreamWriter、文字列生成ならStringWriter

StreamWriterStringWriterでは、出力先が異なります。

クラス出力先主な用途
StreamWriterファイルやストリームログ、CSV、テキストファイル
StringWriterメモリ上の文字列HTML生成、テスト、文字列の組み立て
TextWriter派生クラスによって決まる出力処理の共通化

最終的にファイルを作成したい場合はStreamWriterを使用します。処理結果を文字列として別のAPIへ渡したい場合や、出力内容をテストしたい場合はStringWriterが適しています。

5-3. StringWriterを使った実装例

次の例では、商品情報から簡単なCSV文字列を生成しています。

using System;using System.IO;

class Program{static void Main(){using StringWriter writer = new StringWriter();

    writer.WriteLine("商品名,価格");writer.WriteLine("キーボード,5000");writer.WriteLine("マウス,3000");string csv = writer.ToString();Console.WriteLine(csv);}

}

出力結果は次のようになります。

商品名,価格キーボード,5000マウス,3000

実際のCSVでは、値にカンマ、改行、ダブルクォーテーションが含まれる場合のエスケープ処理も必要です。ただし、StringWriterによって、ファイルを作成せずにCSV形式の文字列を組み立てられる点は同じです。

6. TextWriterを引数にした汎用的な実装例

6-1. TextWriterを使うと出力先を切り替えやすい

次のメソッドは、注文情報をTextWriterへ出力します。

static void WriteOrder(TextWriter writer,string productName,int price){writer.WriteLine("注文情報");writer.WriteLine($"商品名: {productName}");writer.WriteLine($"価格: {price}円");}

メソッド内には、ファイルパスやStreamWriterStringWriterに関する処理がありません。

どこへ出力するかは、呼び出し側が決めます。

6-2. ファイル出力と文字列出力を同じメソッドで扱う

先ほどのWriteOrderメソッドをファイル出力に使う例です。

using TextWriter fileWriter = new StreamWriter("order.txt");

WriteOrder(fileWriter,productName: "ディスプレイ",price: 30000);

同じメソッドを文字列生成にも使えます。

using StringWriter stringWriter = new StringWriter();

WriteOrder(stringWriter,productName: "ディスプレイ",price: 30000);

string result = stringWriter.ToString();Console.WriteLine(result);

さらに、コンソール出力を表すConsole.Outも渡せます。

WriteOrder(Console.Out,productName: "ディスプレイ",price: 30000);

WriteOrderの実装を変更することなく、ファイル、文字列、コンソールへ出力できます。

6-3. テストしやすいコードを書くためのTextWriter活用法

出力処理のテストで実際のファイルを作成すると、ファイルの削除、保存先、アクセス権限などを考慮する必要があります。

引数がTextWriterなら、テスト時にStringWriterを渡して出力結果を確認できます。

using System;using System.IO;

class Program{static void Main(){using StringWriter writer = new StringWriter();

    WriteOrder(writer,productName: "マウス",price: 3000);string actual = writer.ToString();bool containsProduct =actual.Contains("商品名: マウス");bool containsPrice =actual.Contains("価格: 3000円");Console.WriteLine(containsProduct); // TrueConsole.WriteLine(containsPrice);   // True}static void WriteOrder(TextWriter writer,string productName,int price){writer.WriteLine("注文情報");writer.WriteLine($"商品名: {productName}");writer.WriteLine($"価格: {price}円");}

}

この方法なら、テストのたびにファイルを作成する必要がありません。出力処理と保存先の処理を分離できるため、コードの責任も明確になります。

7. TextWriterの非同期書き込み

7-1. WriteAsyncとWriteLineAsyncの使い方

TextWriterには、非同期で書き込むためのWriteAsyncWriteLineAsyncがあります。

using System.IO;

class Program{static async Task Main(){using TextWriter writer =new StreamWriter("async-output.txt");

    await writer.WriteAsync("非同期で");await writer.WriteLineAsync("書き込みます。");await writer.WriteLineAsync("2行目のデータです。");}

}

WriteAsyncは改行なし、WriteLineAsyncは改行付きで書き込みます。

非同期メソッドの完了を待つため、基本的にはawaitを付けて呼び出します。

7-2. 大量データを書き込むときに非同期処理を使うメリット

ファイルやネットワークなどへの書き込みは、入出力の完了待ちが発生する処理です。

同期メソッドを使用すると、書き込みが終わるまで現在の処理が待機します。非同期メソッドを使用すると、待機中のスレッドをほかの処理に利用しやすくなります。

特に次のような場面で役立ちます。

  • Webアプリケーションで応答性を保ちたい

  • GUIアプリケーションの画面停止を避けたい

  • 大きなファイルを出力する

  • ネットワークストリームへ書き込む

  • 多数の入出力処理を扱う

ただし、非同期処理を使えば必ず書き込み速度が速くなるわけではありません。主な目的は、入出力の待機中にスレッドを占有し続けないことです。

7-3. 非同期書き込み時の注意点

非同期メソッドは、原則としてawaitしてから次の書き込みを行います。

await writer.WriteLineAsync("1行目");await writer.WriteLineAsync("2行目");

同じTextWriterに対して、複数のタスクから同時に書き込むことは避けましょう。

// 同じwriterに対する並列書き込みは避けるTask task1 = writer.WriteLineAsync("タスク1");Task task2 = writer.WriteLineAsync("タスク2");

await Task.WhenAll(task1, task2);

TextWriterは、既定では複数スレッドからの同時利用を前提としていません。並列処理が必要な場合は、書き込み部分をロックする、単一の書き込み担当へ集約する、TextWriter.Synchronizedを検討するなどの対策が必要です。

また、大量のデータを1行ずつ非同期で書き込むと、非同期呼び出し自体の回数が増えます。必要に応じて、ある程度のデータをまとめて書き込む設計も検討しましょう。

8. TextWriterを使うときの注意点

8-1. TextWriterは直接newできない

TextWriterは抽象クラスなので、次のように直接生成できません。

TextWriter writer = new TextWriter();

用途に応じて、派生クラスを生成します。

TextWriter fileWriter =new StreamWriter("output.txt");

TextWriter stringWriter =new StringWriter();

「変数の型をTextWriterにすること」と「TextWriter自体を生成すること」は別です。

8-2. Dispose忘れによるファイルロックに注意する

StreamWriterを破棄しないままにすると、内部バッファーの内容が完全に書き込まれなかったり、ファイルハンドルが解放されなかったりする可能性があります。

次のような書き方は避けましょう。

StreamWriter writer = new StreamWriter("output.txt");writer.WriteLine("データ");

// Disposeされていない

基本的にはusingを使用します。

using StreamWriter writer =new StreamWriter("output.txt");

writer.WriteLine("データ");

メソッド内で生成したStreamWriterをフィールドに保存する場合は、そのクラス側で適切にDisposeする設計が必要です。

8-3. 文字化けを防ぐためにエンコーディングを意識する

テキストファイルは、文字を一定のルールでバイト列に変換して保存します。その変換ルールが文字コードです。

書き込み時と読み込み時の文字コードが異なると、文字化けの原因になります。

using TextWriter writer = new StreamWriter("output.txt",append: false,encoding: Encoding.UTF8);

外部システムとファイルを受け渡す場合は、次の点を確認しましょう。

  • UTF-8か、別の文字コードか

  • BOMが必要か

  • 改行コードに指定があるか

  • 読み込み側が対応しているか

特に古いシステムや表計算ソフトとの連携では、仕様を事前に確認することが重要です。

8-4. 例外発生時にもリソースを解放できる書き方にする

手動でCloseするコードでは、Closeより前に例外が発生するとリソースを解放できない可能性があります。

StreamWriter writer = new StreamWriter("output.txt");

writer.WriteLine("データ");SomeMethodThatMayThrow();

writer.Close();

usingを使用すると、途中で例外が発生しても破棄処理が実行されます。

using StreamWriter writer =new StreamWriter("output.txt");

writer.WriteLine("データ");SomeMethodThatMayThrow();

ファイルやストリームを扱うときは、「最後に手動で閉じる」よりも、「usingによって自動的に破棄する」と覚えておくと安全です。

9. TextWriterに関するよくある質問

9-1. TextWriterとFileStreamの違いは?

TextWriterは文字を出力するための抽象クラスです。FileStreamは、ファイルに対してバイト単位で読み書きするためのストリームです。

using FileStream stream = new FileStream("data.bin",FileMode.Create,FileAccess.Write);

byte[] data = { 0x41, 0x42, 0x43 };stream.Write(data, 0, data.Length);

FileStreamはバイト列を扱うため、画像、動画、圧縮データなどのバイナリファイルにも使用できます。

一方、テキストを簡単に書き込む場合はStreamWriterを使用します。

using TextWriter writer =new StreamWriter("data.txt");

writer.WriteLine("ABC");

StreamWriterは、文字列を指定された文字コードでバイト列に変換し、内部のストリームへ書き込みます。

9-2. Console.OutもTextWriterなの?

はい。Console.Outの型はTextWriterです。

TextWriter writer = Console.Out;

writer.WriteLine("コンソールに出力されます。");

そのため、TextWriterを引数に取るメソッドへConsole.Outを渡せます。

static void PrintResult(TextWriter writer){writer.WriteLine("処理結果: 成功");}

PrintResult(Console.Out);

この仕組みを使うと、同じメソッドの出力先をコンソール、ファイル、文字列などへ切り替えられます。

9-3. StreamWriterだけ使えば十分?

ファイルへ文字を書き込むだけなら、StreamWriterだけでも十分です。

using StreamWriter writer =new StreamWriter("output.txt");

writer.WriteLine("データ");

ただし、メソッドの引数までStreamWriterにすると、そのメソッドはストリームへの出力に強く依存します。

static void Output(StreamWriter writer){writer.WriteLine("データ");}

TextWriter型にすれば、StringWriterConsole.Outも渡せます。

static void Output(TextWriter writer){writer.WriteLine("データ");}

小規模で単純な処理ではStreamWriterを直接使い、再利用性やテストのしやすさが必要な部分ではTextWriterを使うとよいでしょう。

9-4. TextWriterは読み込みにも使える?

いいえ。TextWriterは書き込み専用です。

テキストを読み込む場合は、TextReaderや、その派生クラスであるStreamReaderStringReaderを使用します。

ファイルを読み込む例は次のとおりです。

using TextReader reader =new StreamReader("input.txt");

string? content = reader.ReadToEnd();

Console.WriteLine(content);

文字列を読み込む場合はStringReaderを使用できます。

using TextReader reader =new StringReader("1行目\n2行目");

string? line;

while ((line = reader.ReadLine()) is not null){Console.WriteLine(line);}

役割を整理すると、次のようになります。

処理抽象クラス主な実装クラス
テキストの書き込みTextWriterStreamWriterStringWriter
テキストの読み込みTextReaderStreamReaderStringReader

まとめ

C#のTextWriterは、文字を出力するための共通機能を定義した抽象クラスです。実際の書き込みには、ファイルやストリームへ出力するStreamWriter、メモリ上に文字列を生成するStringWriterなどを使用します。

単純なファイル出力ではStreamWriterを直接使用しても問題ありません。一方、メソッドの引数をTextWriterにすると、ファイル、文字列、コンソールなどの出力先を柔軟に切り替えられます。

利用時には、usingによる確実なリソース解放、適切な文字コードの指定、非同期メソッドのawait、同じライターへの並列書き込みを避けることが重要です。

TextWriterを「出力先に関係なく文字を書き込める共通の窓口」と理解すると、StreamWriterStringWriterとの関係も分かりやすくなります。