【C#】文字列操作を高速化するStringBuilderの利用方法

C#

C#で文字列の操作を行う際、stringクラスを利用することが一般的です。しかし、stringクラスは不変(immutable)であり、文字列を変更するたびに新しい文字列がメモリ上に生成されるため、大量の文字列操作を行うとパフォーマンスの低下を招くことがあります。この問題を解決するためには、StringBuilderクラスを使用します。StringBuilderは可変(mutable)であり、文字列を効率的に追加、削除、変更できるため、繰り返しの文字列操作に適しています。

この記事では、StringBuilderの基本的な使い方と、stringクラスに比べてStringBuilderがどのように優位であるかを、サンプルコードと共に紹介します。

サンプルコード

以下のサンプルコードは、ファイルからテキストを読み込み、それを加工して別のファイルに書き出す簡単なプログラムです。このプロセスでは、StringBuilderを使用して文字列の加工を行います。

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

class Program
{
    static void Main(string[] args)
    {
        // ファイルパスの設定
        string inputFilePath = @"C:\path\to\your\input.txt";
        string outputFilePath = @"C:\path\to\your\output.txt";

        try
        {
            // ファイルを読み込むためのStreamReaderを作成
            using (StreamReader reader = new StreamReader(inputFilePath))
            {
                // ファイルを書き込むためのStreamWriterを作成
                using (StreamWriter writer = new StreamWriter(outputFilePath))
                {
                    // StringBuilderのインスタンスを作成
                    StringBuilder sb = new StringBuilder();

                    // ファイルから1行ずつ読み込み、StringBuilderに追加
                    string line;
                    while ((line = reader.ReadLine()) != null)
                    {
                        // 文字列を加工してStringBuilderに追加
                        sb.AppendLine(line.ToUpper()); // 大文字に変換
                    }

                    // StringBuilderの内容をファイルに書き込み
                    writer.Write(sb.ToString());
                }
            }

            Console.WriteLine("ファイルの加工が完了しました。");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"エラーが発生しました: {ex.Message}");
        }
    }
}

パフォーマンスの比較

以下は、stringクラスとStringBuilderクラスを使用して文字列の連結を行い、そのパフォーマンスを測定するプログラムのサンプルコードです。

このプログラムでは、stringクラスとStringBuilderクラスの両方で10,000回の文字連結を行い、それぞれ5回実行した際の平均実行時間を計測しています。

using System;
using System.Diagnostics;
using System.Text;

class Program
{
    private const int CountMax = 10000; // 連結回数
    private const int TrialNum = 5;     // 計測回数

    static void Main(string[] args)
    {
        // 測定条件を表示
        Console.WriteLine($"{CountMax.ToString("#,##0")}回の連結、{TrialNum}回の平均値");

        // stringのパフォーマンス測定
        double stringConcatTime = MeasureStringConcatenation();
        Console.WriteLine($"stringの平均連結時間: {stringConcatTime}ミリ秒");

        // StringBuilderのパフォーマンス測定
        double stringBuilderConcatTime = MeasureStringBuilderConcatenation();
        Console.WriteLine($"StringBuilderの平均連結時間: {stringBuilderConcatTime}ミリ秒");
    }

    private static double MeasureStringConcatenation()
    {
        Stopwatch stopwatch = new Stopwatch();
        double totalTime = 0;

        for (int trial = 0; trial < TrialNum; trial++)
        {
            stopwatch.Restart();
            string result = "";
            for (int i = 0; i < CountMax; i++)
            {
                result += "a"; // 文字列の連結
            }
            stopwatch.Stop();
            totalTime += stopwatch.Elapsed.TotalMilliseconds;
        }

        return totalTime / TrialNum; // 平均時間を計算
    }

    private static double MeasureStringBuilderConcatenation()
    {
        Stopwatch stopwatch = new Stopwatch();
        double totalTime = 0;

        for (int trial = 0; trial < TrialNum; trial++)
        {
            stopwatch.Restart();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < CountMax; i++)
            {
                sb.Append("a"); // 文字列の連結
            }
            string result = sb.ToString();
            stopwatch.Stop();
            totalTime += stopwatch.Elapsed.TotalMilliseconds;
        }

        return totalTime / TrialNum; // 平均時間を計算
    }
}

実行結果

1万回の連結において、StringBuilderの方が100倍以上高速に動作することが分かります。

まとめ

C#における文字列操作は、stringクラスとStringBuilderクラスを用いて行うことができます。stringクラスはそのシンプルさから直感的なコードを書くことができる一方で、不変性のために大量の文字列操作を伴う場合にはパフォーマンスの問題が生じる可能性があります。本記事では、このような状況でStringBuilderクラスの使用がいかに有効であるかを、サンプルコードを通じて紹介しました。

タイトルとURLをコピーしました