ここではStringBuilderクラスついて見ていきます。 StringBuilderクラスを用いることにより、直接文字列を操作するよりも効率的に文字列の加工・編集を行うことができます。
StringBuilderクラスの特徴
.NET FrameworkのStringクラスはインスタンスの持つ値を変更できないという性質を持つ不変な型です。 Stringクラスの文字列操作ではインスタンス自体が持っている値(=文字列)が変わることは無く、操作の度に結果が格納された新しい文字列のインスタンスが生成されます。
一方、StringBuilderクラスは内部にChar配列のバッファを保持し、文字列操作によってインスタンスの持つ値(=文字列)が変わる可変な型です。 StringBuilderクラスの文字列操作では、操作の際にバッファの内容のみが変更され、新しい文字列のインスタンスは生成されません。
このような特徴から、文字列の連結・挿入・削除・置換といった加工・編集を繰り返し行う場合は、StringBuilderクラスを用いて行った方が適しています。 (実際にどの程度パフォーマンスに差が出るかは§.StringBuilderのパフォーマンスで検証しています。)
一方でStringBuilderクラスにはStartsWith・IndexOf・Equalsといった文字列の探索・比較を行うメソッドは用意されていません。 探索・比較の目的にはStringクラスのメソッドやRegexクラスを使用します。
StringBuilderクラスとStringクラスの違い
まずはStringBuilderクラスとStringクラスで同じ操作を行う場合の違いを見ておきます。 詳細な使い方は後述します。
このように、Stringクラスの文字列操作ではその都度新しいインスタンスが生成されますが、StringBuilderクラスでは単一のインスタンスを使いまわして文字列操作を繰り返すことができます。
StringBuilderクラスで加工した文字列は、最終的にStringBuilder.ToStringメソッドを使うことにより通常の文字列として取得出来ます。
StringBuilderによる文字列操作
文字列の追加 (Append, AppendLine)
StringBuilder.Appendメソッドは、バッファの末尾に文字列を追加するメソッドで、String.Concatメソッドや加算演算子+
による文字列の連結操作に相当します。
また、StringBuilder.AppendLineメソッドは、バッファの末尾に文字列を追加する点はAppendメソッドと同様ですが、自動的に改行文字も追加されます。 AppendLineメソッドに引数を指定しなかった場合は、改行文字のみが追加されます。 (Appendメソッド・AppendLineメソッドはConsole.Writeメソッド・WriteLineメソッドと似た動作と考えてよいでしょう)
AppendLineメソッドでは常にEnvironment.NewLineが改行文字として使われます。 異なる改行文字を使用する方法については§.StringBuilderと改行文字で後述します。
StringBuilderと改行文字
AppendLineメソッドでは、常にEnvironment.NewLineが改行文字として使用されます。 そのため、実行環境によって改行文字が変わると不都合がある困る場合や、CRまたはLFのみを追加したいといった場合は、AppendLineメソッドの代わりにAppendメソッドを使い、個別に改行文字を書き込む必要があります。
上記の実行結果ではわかりやすさのために出力結果に改行を加えています。
これとは別に、StringWriterクラスを使うことによっても、StringBuilderで任意の改行文字を使うようにすることができます。 StreamWriter.NewLineプロパティを変更すると、書き込まれる改行文字を変更することができます。 次の例では、結果が分かりやすいように改行文字を"↵\r\n"に変更しています。 もちろん、改行文字にCRまたはLFのみを指定することもできます。
StringWriterクラスについて詳しくはStringReaderクラス/StringWriterクラスをご覧ください。
書式を指定した文字列の追加 (AppendFormat)
StringBuilder.AppendFormatメソッドは、指定された書式に整形してからバッファに追加するメソッドです。 String.Formatメソッドによる書式化と文字列の連結を同時に行う操作に相当します。
AppendFormatメソッドでは、String.FormatメソッドやConsole.WriteLineメソッドなどと同様に書式指定子を指定することができます。
AppendFormatメソッドでは改行文字は追加されません。 AppendFormatLineのようなメソッドもないので、改行文字を追加したい場合はAppendLineメソッドと組み合わせて使う必要があります。
書式化について詳しくは文字列と書式を参照してください。
AppendFormatメソッドでは任意の型の引数を書式化して追加することが出来ますが、Appendメソッドも同様に文字列以外の値を追加することが出来ます。 この際、引数として指定された値は既定の書式で自動的に文字列化されてから追加されます。
文字列の挿入・削除・置換 (Insert, Remove, Replace)
Stringクラスと同様、文字列の挿入・削除・置換にはStringBuilder.Insert、StringBuilder.Remove、StringBuilder.Replaceの各メソッドが使えます。 引数と動作はStringクラスのものとほぼ同じですが、InsertメソッドはAppendメソッド同様に文字列以外も追加出来ます。 なお、String.Removeメソッドとは異なり、StringBuilder.Removeメソッドでは削除する文字数を省略することは出来ません。
StringBuilderの内容を完全に削除したい場合は、Clearメソッドを使うこともできます。
メソッドチェイン
Append, AppendLine, AppendFormat, Insert, Remove, Replaceの各メソッドは、戻り値としてインスタンス自身を返します。 これにより、stringクラスと同様にメソッドチェインを記述出来ます。
バッファの操作
インスタンスの内容が不変であるStringクラスとは異なり、内容が可変であるStringBuilderクラスでは内部にサイズが変更可能なバッファを備えています。 ここではStringBuilderのバッファの操作を行うプロパティ・メソッドについて見ていきます。
文字列の長さとバッファのクリア (Length, Clear)
StringBuilderに格納されている文字数はLengthプロパティで取得できます。 String.Lengthプロパティとは異なり、StringBuilderではLengthプロパティを設定することによって文字列の長さを変更することができます。 また、Lengthプロパティを0にすることにより、バッファの内容をクリアすることが出来ます。
Lengthを現在の長さより小さくすると文字列が切り詰められ、逆に大きくした場合はヌル文字(U+0000)で埋められます。
.NET Framework 4からは、バッファのクリアにStringBuilder.Clearメソッドを使うことが出来ます。 結果はLengthに0を指定した場合と同じです。
バッファの容量 (Capacity, EnsureCapacity)
StringBuilderクラスでは、常に文字列の実際の長さ以上のバッファが確保されています。 Capacityプロパティは現在のバッファのサイズ(容量)を取得するためのプロパティです。 このプロパティを設定することでバッファのサイズを変更することもできます。 ただし、当然現在の文字列の長さ未満にすることはできません。 この場合、例外ArgumentOutOfRangeExceptionがスローされます。
また、EnsureCapacityメソッドでもバッファのサイズを変更することができます。 このメソッドは、容量を減らすためではなく、現在の容量よりも多く確保したい場合に使用します。 EnsureCapacityメソッドでバッファを確保する場合、実際に確保される容量が指定した容量よりも大きくなる場合があります。 つまり、 最小でもメソッドで指定したサイズが確保されます。
StringBuilderのコンストラクタでも容量の初期値を指定してインスタンスを生成することができます(指定しない場合は初期値16でバッファが確保されます)。 StringBuilderに追加しようとする文字列の量をあらかじめ予測出来る場合は、あらかじめ適切な容量のバッファを確保しておくことでバッファ確保によるオーバーヘッドを減らすことが出来ます。
バッファの最大容量 (MaxCapacity)
StringBuilderが確保するバッファの最大容量を指定することも出来ます。 最大容量を指定するには、コンストラクタで指定する必要があります。 特に指定しない場合は初期値Int32.MaxValue(2,147,483,647)が最大容量に指定されます。 インスタンスの最大容量はMaxCapacityプロパティで取得することが出来ます。
文字列を追加しようとしたときやバッファのサイズを変更しようとしたときなど、確保しようとしているバッファの容量が最大容量を超える場合は例外ArgumentOutOfRangeExceptionがスローされます。
StringBuilderのパフォーマンス
StringクラスとStringBuilderクラスの最大の違いは、文字列操作の都度インスタンスが生成されるかどうかですが、これによりパフォーマンスにも違いが現れます。 StringクラスとStringBuilderクラスを用いて文字列の追加を行い、実行時間にどれくらいの差があるかを比較してみます。 次のコードでは、文字列の追加を10,000回行っています。 あらかじめStringBuilderの容量を指定している点にも注目してください。
結果のとおり、実行時間に大幅な差があります。
続いて、文字列の置換で比較してみます。 次のコードでは文字列の置換を10,000回行っています。 文字列の中間にある文字を置換し、かつ置換した結果文字列が長くなるようにしています。
先の例ほどの差にはなりませんが、それでもStringBuilderを用いたほうがStringよりも実行時間が短くなっています。 このように文字列操作を繰り返し行う場合は、StringBuilderを使うことでより短い時間で処理を終わらせることが期待できます。