閉じる

【C#】StringWriterとStringReaderの使い方・活用法を徹底解説

C#でのプログラミングにおいて、文字列の操作は避けて通れない要素です。

通常、文字列の結合にはstring.ConcatStringBuilderが使われますが、ストリーム(Stream)に近い感覚で文字列を書き込んだり、読み込んだりしたい場面があります。

そのような時に重宝するのがStringWriterクラスStringReaderクラスです。

これらは、メモリ上の文字列をあたかも「ファイル」のように扱うための橋渡し役として機能します。

本記事では、初心者から中級者の方に向けて、これら2つのクラスの基本的な概念から、実務で役立つ応用的な活用法までを詳しく解説していきます。

C#におけるStringWriterとStringReaderの役割

C#の入出力処理(I/O)では、TextWriterTextReaderという抽象クラスが基盤となっています。

これらは本来、ファイルやネットワークストリームに対して文字を書き込んだり読み込んだりするための共通インターフェースを提供します。

StringWriterとStringReaderは、この入出力の仕組みを「文字列(string)」に対して適用したものです。

TextWriterとTextReaderの継承関係

まず理解しておくべきは、これらがどのクラスを継承しているかという点です。

StringWriterTextWriterを、StringReaderTextReaderをそれぞれ継承しています。

クラス名親クラス役割
StringWriterTextWriter文字列(StringBuilder)に対してテキストを書き込む
StringReaderTextReader文字列からテキストを1文字、または1行ずつ読み込む

この継承関係のおかげで、「ファイルへの書き込みを想定して作られたメソッド」に対して、ファイル名の代わりにStringWriterを渡すことで、結果を直接文字列として受け取るといった柔軟なプログラミングが可能になります。

StringWriterの基本的な使い方と仕組み

StringWriterは、内部でStringBuilderを保持しており、そこに文字を蓄積していく仕組みになっています。

主に、複雑なフォーマットの文字列を構築する場合や、ストリーム出力を要求する外部ライブラリとの連携に使用されます。

StringBuilderとの密接な関係

StringWriterのコンストラクタには、既存のStringBuilderを渡すことも可能です。

何も指定しない場合は、内部で自動的に新しいインスタンスが作成されます。

基本的な書き込みのコード例

以下のサンプルコードでは、StringWriterを使用して複数の行を書き込み、最終的に一つの文字列として出力する方法を示します。

C#
using System;
using System.IO;
using System.Text;

class Program
{
    static void Main()
    {
        // StringWriterのインスタンスを生成
        // usingステートメントを使用することで確実にリソースを解放します
        using (StringWriter writer = new StringWriter())
        {
            // 文字列の書き込み
            writer.WriteLine("--- レポート開始 ---");
            writer.WriteLine("作成日: {0}", DateTime.Now.ToString("yyyy/MM/dd"));
            writer.WriteLine("ステータス: 正常");
            
            // 追記
            writer.Write("詳細情報: ");
            writer.Write("システムチェック完了。");
            
            // 内部のStringBuilderに蓄積された内容をstringとして取得
            string result = writer.ToString();
            
            // 結果を表示
            Console.WriteLine(result);
        }
    }
}
実行結果
--- レポート開始 ---
作成日: 2026/01/16
ステータス: 正常
詳細情報: システムチェック完了。

なぜStringBuilderではなくStringWriterを使うのか

一見すると「StringBuilderで十分ではないか」と感じるかもしれません。

しかし、StringWriterの真価は、TextWriterを引数に取る既存のメソッドを活用できる点にあります。

例えば、XMLのシリアライズ(オブジェクトをXML形式の文字列に変換する処理)を行う際、多くのライブラリは出力先としてTextWriterを要求します。

ここでStringWriterを渡すことで、ファイルを作らずにメモリ内でXML文字列を生成できるのです。

StringReaderによる文字列の効率的な読み込み

次に、StringReaderについて見ていきましょう。

これはStringWriterとは逆で、既存の巨大な文字列をストリームとして扱い、1行ずつ、あるいは特定の文字数ずつ読み取るために使用します。

行単位での読み込み処理

例えば、ログデータやCSV形式の文字列を受け取った際、string.Split('\n')を使って配列に分割することもできますが、非常に大きな文字列の場合、配列を作成するとメモリを大量に消費してしまいます。

StringReaderを使えば、メモリ効率良く1行ずつ処理できます。

文字列の読み取りコード例

C#
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string multiLineData = "ID,名前,スコア\n1,田中,85\n2,佐藤,92\n3,鈴木,78";

        // StringReaderを使用して文字列を読み込む
        using (StringReader reader = new StringReader(multiLineData))
        {
            string line;
            int lineCount = 0;

            // ReadLine()は行がなくなるとnullを返します
            while ((line = reader.ReadLine()) != null)
            {
                lineCount++;
                Console.WriteLine("行 {0}: {1}", lineCount, line);
            }
        }
    }
}
実行結果
行 1: ID,名前,スコア
行 2: 1,田中,85
行 3: 2,佐藤,92
行 4: 3,鈴木,78

ReadメソッドとPeekメソッド

StringReaderには、行単位以外にも詳細な読み取りメソッドが用意されています。

  • Read(): 次の1文字を読み込み、文字コードを返します。読み取る文字がない場合は-1を返します。
  • Peek(): 次の1文字を確認しますが、読み取り位置を進めません。「次に何が来るか」を確認してから処理を分岐させたい時に便利です。
  • ReadToEnd(): 現在の位置から最後までを一気に読み込みます。

応用編:外部ライブラリやシリアル化での活用

実務で最もStringWriterが登場する場面の一つが、シリアル化(Serialization)です。

ここでは、C#標準のXmlSerializerを用いた例を紹介します。

XMLシリアライザーとの連携

通常、XmlSerializer.Serializeメソッドは、第1引数に出力先のストリームやTextWriterを求めます。

ここにStringWriterを指定します。

C#
using System;
using System.IO;
using System.Xml.Serialization;

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}

class Program
{
    static void Main()
    {
        User user = new User { Name = "Tech太郎", Age = 25 };

        // XMLシリアライザーの準備
        XmlSerializer serializer = new XmlSerializer(typeof(User));

        // StringWriterを使ってメモリ内でXMLを作成
        using (StringWriter sw = new StringWriter())
        {
            serializer.Serialize(sw, user);
            
            // 生成されたXMLを表示
            string xmlContent = sw.ToString();
            Console.WriteLine("生成されたXML:");
            Console.WriteLine(xmlContent);
        }
    }
}
XML
<?xml version="1.0" encoding="utf-16"?>
<User xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Tech太郎</Name>
  <Age>25</Age>
</User>

このように、本来ファイル出力を想定しているAPIに対して、「出力を文字列として横取りする」というテクニックは非常に強力です。

これは単体テスト(Unit Test)で、コンソール出力やファイル出力を検証したい場合にも応用できます。

StringWriter/ReaderとStringBuilderの使い分け

最後に、よくある疑問である「いつ何を使えばいいのか」を整理します。

StringBuilderは純粋な文字列の加工に特化していますが、StringWriterはその加工を「ストリーム」という枠組みで行うためのものです。

特徴StringBuilderStringWriter / StringReader
主な用途文字列の頻繁な結合・編集ストリームベースのAPIとの連携
抽象化文字列操作に特化TextWriter / TextReaderとして振る舞う
メモリ効率高い非常に高い(StringReaderでの逐次読み込み時)
主なメソッドAppend, Insert, ReplaceWrite, WriteLine / ReadLine, ReadToEnd

使い分けの指針:

  1. ループ内で単純に文字列を繋げたいだけなら、StringBuilderを選びます。
  2. TextWriterTextReaderを引数に取るメソッド(SerializeSaveなど)を使いたいなら、StringWriterを選びます。
  3. 複数行にわたる巨大な文字列を、配列化せずに一行ずつ丁寧に処理したいなら、StringReaderを選びます。

特に現代的なアプリケーション開発では、Web APIのレスポンスや設定ファイルの処理など、文字列データをストリームとして扱う機会が増えています。

これらのクラスを正しく使い分けることで、メモリ消費を抑えた、拡張性の高いコードを書くことができるようになります。

まとめ

StringWriterとStringReaderは、一見すると地味なクラスですが、C#のI/Oシステムと文字列操作を繋ぐ非常に重要な役割を担っています。

StringWriterを使えば、ファイルへの書き込みと同じ作法で、メモリ上に柔軟な文字列を構築できます。

特にXMLやJSON、カスタムログの生成において、既存のTextWriter向けAPIをそのまま利用できるメリットは計り知れません。

また、StringReaderを活用することで、巨大なテキストデータをメモリ効率良く、安全に読み進めることが可能になります。

「文字列を単なる値として扱う」段階から一歩進んで、「文字列をデータ流(ストリーム)として扱う」という考え方を取り入れることで、あなたのC#プログラミングの幅はより一層広がるでしょう。

今回紹介したコード例を参考に、ぜひ自身のプロジェクトでも活用してみてください。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!