StringBuilderクラス
文字列の高速な編集
文字列(string型)同士の結合は+
演算子やstring.Concat
メソッドを使用します。
通常はこれで十分ですが、C#ではstring型のデータは変更ができないという特徴があります。
つまり単純な文字列結合であっても新しい文字列オブジェクトを生成しているため、その分のコストがやや掛かります。
通常は問題になるようなコストではありませんが、同じ変数に対して何度も文字列結合を繰り返して文字列を構築する場合はStringBuilder
クラスを使用したほうが高速になる可能性があります。
(System.Text
名前空間)
StringBuilderクラスは文字列の結合時に新しいオブジェクトは生成せず、既存のオブジェクトに直接結合していきます。
string s = "a";
//時間計測のためのクラス
var sw = new System.Diagnostics.Stopwatch();
//+演算子による結合
{
string str1 = "";
//計測スタート
sw.Start();
//10万回繰り返し
for (int i = 0; i < 100000; i++)
{
str1 += s;
}
//計測ストップ
sw.Stop();
//経過時間を表示
Console.WriteLine(sw.Elapsed);
}
//経過時間をリセット
sw.Reset();
//StringBuilderによる結合
{
StringBuilder sb = new StringBuilder();
//計測スタート
sw.Start();
//10万回繰り返し
for (int i = 0; i < 100000; i++)
{
sb.Append(s);
}
//StringBuilderをstring型に変換
string str2 = sb.ToString();
//計測ストップ
sw.Stop();
//経過時間を表示
Console.WriteLine(sw.Elapsed);
}
00:00:00.9545671 00:00:00.0005509
実行結果は環境によって異なります。
System.Diagnostics.Stopwatch
クラスは時間計測のためのクラスです。
これを利用して文字列結合に掛かった時間を計測します。
使い方はサンプルコード中のコメントを参照してください。
まずStringBuilderクラスのインスタンスをnewで生成します。
このインスタンスに対して文字列操作を行います。
文字列の結合はAppend
メソッドで行います。
引数はstring型以外にも組み込み型(int型やdouble型など)も指定できます。
(その他のクラスはToString
メソッドが呼び出されます)
StringBuilderクラスのインスタンスはそのままでは文字列として使えないので、必要な処理が終わったらToString
メソッドでstring型に変換します。
Capacityプロパティ
StringBuilderクラスは内部的に文字列を結合するためのメモリ領域をあらかじめ確保しています。
その領域のサイズを示すのがCapacity
プロパティです。
型はint型です。
結合によってCapacity
プロパティ以上の文字列の長さになると、自動的に新しいメモリ領域が確保されます。
Microsoftの実装では初期値が「16」で、これを超える文字数の結合が発生する度に倍々に領域が増えていくようです。
(16→32→64→128...)
ただし今後のバージョンアップによって変わる可能性はあります。
Capacity
プロパティはStringBuilderクラスのインスタンス生成時にあらかじめ指定することができます。
メモリの確保はそこそこコストが掛かる処理なので、どの程度の文字数になるかがあらかじめわかっている場合は、最初に確保してしまった方が処理が高速になる可能性があります。
//65535文字分の領域をあらかじめ確保
StringBuilder sb = new StringBuilder(65535);
StringBuilderの内容をクリアする
StringBuilderのインスタンスの中身をクリアするには
Length
プロパティに0を代入Clear
メソッドを使用する(.NET Framework 4.0以降)- 新しいインスタンスを代入する
のいずれかの方法で行います。
StringBuilder sb = new StringBuilder();
sb.Append("This is a pen.");
sb.Length = 0;
sb.Clear();
sb = new StringBuilder();
どの方法でも大した差はないので好みの問題です。
しかし前二つの方法ではCapacity
プロパティはそのままで、メモリ領域は確保されたままになることに注意が必要です。
必要に応じてCapacity
プロパティもセットしておきます。
StringBuilder sb = new StringBuilder();
sb.Append("This is a pen.");
sb.Length = 0;
sb.Capacity = 16;
新しいインスタンスを代入する場合はCapacity
プロパティも既定値にセットされます。
コンストラクター
インスタンス生成時に以下のコンストラクターを使用できます。
//デフォルトの設定
StringBuilder sb1 = new StringBuilder();
//Capacityの設定
StringBuilder sb2 = new StringBuilder(255);
//文字列の初期値の設定
StringBuilder sb3 = new StringBuilder("abc");
//Capacity、MaxCapacityの設定
StringBuilder sb4 = new StringBuilder(255, 65535);
//文字列の初期値とCapacityの設定
StringBuilder sb5 = new StringBuilder("abc", 255);
//文字列の初期値、Capacity、MaxCapacityの設定
StringBuilder sb6 = new StringBuilder("abc", 255);
MaxCapacity
プロパティは読み取り専用のプロパティで、使用可能な最大容量を表します。
コンストラクタ生成時に設定可能ですが、文字列の追加等の処理で自動的に書き換わる可能性があります。
メソッド
StringBuilderクラスは文字列を整形するメソッドも用意されています。
ただ、単純な文字列操作の目的でStringBuilderクラスは使用すべきではありません。
StringBuilderクラスはインスタンスの生成およびstring型への変換処理があるため、その点はstring型に比べて余計なコストがかかります。
StringBuilderは同一オブジェクトに対してループ文などで何十何百と文字列操作を行う場合に有効なクラスです。
Append
Append
メソッドは、末尾に文字列を追加します。
StringBuilder sb = new StringBuilder();
sb.Append("abc"); //string型を追加
sb.Append(123); //int型を追加
Console.WriteLine(sb.ToString());
//'x'を3つ追加
sb.Append('x', 3);
Console.WriteLine(sb.ToString());
//char型配列の1番目から3つ分を追加
sb.Append(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3);
Console.WriteLine(sb.ToString());
//"ABCDEFG"の1文字目から3文字分を追加
sb.Append("ABCDEFG", 1, 3);
Console.WriteLine(sb.ToString());
//いったんクリア
sb = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
sb2.Append("012345");
//StringBuilderインスタンスを追加
sb.Append(sb2);
Console.WriteLine(sb.ToString());
//sb2の1文字目から3文字分を追加
sb.Append(sb2, 1, 3);
Console.WriteLine(sb.ToString());
abc123 abc123xxx abc123xxxbcd abc123xxxbcdBCD 012345 012345123
文字列(string型)のほか、C#の組み込み型(int型など)、および別のStringBuilder型のインスタンスが追加可能です。
string型、char型の配列、StringBuilder型のインスタンスはコピー開始位置とコピーする文字数を指定して部分コピーが可能です。
その他、char型は同じ文字を指定の数だけ連続して追加することができます。
AppendLine
AppendLine
メソッドは、末尾に文字列と改行を追加します。
引数は空(改行のみが追加される)かstring型が指定可能です。
組み込み型などはToString
メソッドで文字列に変換してから追加できます。
StringBuilder sb = new StringBuilder();
sb.AppendLine("abc");
sb.AppendLine(123.ToString());
Console.WriteLine(sb.ToString());
abc 123
AppendJoin
AppendJoin
メソッドは、区切り文字を使用して配列を結合し、末尾に追加します。
このメソッドは.NET Frameworkでは使用できません。
string[] strs = new string[] { "abc", "def" };
StringBuilder sb = new StringBuilder();
sb.AppendJoin('-', strs);
sb.AppendLine();
sb.AppendJoin(", ", strs);
sb.AppendLine();
int[] nums = new int[] { 1, 2, 3 };
sb.AppendJoin(", ", nums.Select(x => x.ToString()));
Console.WriteLine(sb.ToString());
abc-def abc, def 1, 2, 3
第一引数は区切り文字の指定で、char型かstring型を指定できます。
第二引数は配列のほか、IEnamerable型を指定することもできます。
(その派生クラスであるListクラスなども指定できる)
第二引数の要素数が1つ以下の場合は区切り文字は使用されません。
AppendFormat
AppendFormat
メソッドは、フォーマットを指定して文字列を追加します。
フォーマットの動作はstring.Format
メソッドと同等です。
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}-{1}-{2}", 1, 'a', "ABC");
Console.WriteLine(sb.ToString());
1-a-ABC
Insert
Insert
メソッドは、指定の位置に文字列を挿入します。
StringBuilder sb = new StringBuilder();
sb.Insert(0, "abc");
sb.Insert(0, 123);
Console.WriteLine(sb.ToString());
//"AB"を3つ追加
sb.Insert(0, "AB", 3);
Console.WriteLine(sb.ToString());
//いったんクリア
sb = new StringBuilder();
//char型配列を追加
sb.Insert(0, new char[] { 'a', 'b', 'c', 'd', 'e' });
Console.WriteLine(sb.ToString());
//char型配列の1番目から3つ分を追加
sb.Insert(0, new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3);
Console.WriteLine(sb.ToString());
123abc ABABAB123abc abcde bcdabcde
第一引数に挿入位置を指定する以外は基本的にAppend
メソッドと同じですが、いくつかのオーバーロードが異なります。
string型やStringBuilder型の部分コピーはできません。
Remove
Remove
メソッドは、文字列の一部を削除します。
StringBuilder sb = new StringBuilder("0123456789");
//2文字目から3文字分を削除
sb.Remove(2, 3);
Console.WriteLine(sb.ToString());
0156789
第一引数は削除開始位置、第二引数は削除する文字数です。
Replace
Replace
メソッドは、文字列の一部を別の文字、文字列で置き換えます。
StringBuilder sb1 = new StringBuilder("This is his pen.");
StringBuilder sb2 = new StringBuilder("This is his pen.");
StringBuilder sb3 = new StringBuilder("This is his pen.");
StringBuilder sb4 = new StringBuilder("This is his pen.");
//全ての'i'を'x'に置き換え
sb1.Replace('i', 'a');
//全ての"is"を"at"に置き換え
sb2.Replace("is", "at");
//5文字目から2文字分の範囲の'i'を'x'に置き換え
sb3.Replace('i', 'a', 5, 2);
//5文字目から2文字分の範囲の"is"を"at"に置き換え
sb4.Replace("is", "at", 5, 2);
Console.WriteLine(sb1.ToString());
Console.WriteLine(sb2.ToString());
Console.WriteLine(sb3.ToString());
Console.WriteLine(sb4.ToString());
Thas as has pen. That at hat pen. This as his pen. This at his pen.
Clear
Clear
メソッドは、インスタンス内の文字列を消去します。
このメソッドは.NET Framework4以降で使用可能です。
(.NET Coreはさ1.0から使用可能)
StringBuilder sb = new StringBuilder("This is a pen.");
sb.Clear();
Console.WriteLine(sb.ToString());
.NET Framework3.5まではLength
プロパティに0を代入することで同等の動作となります。
ToString
ToString
メソッドは、インスタンス内の文字列をstring型に変換します。
StringBuilder sb = new StringBuilder("0123456789");
string s1 = sb.ToString();
//1番目から3文字分を変換
string s2 = sb.ToString(1, 3);
Console.WriteLine(s1);
Console.WriteLine(s2);
0123456789 123
CopyTo
CopyTo
メソッドは、インスタンス内の文字列の一部を配列またはSpan<char>
型にコピーします。
StringBuilder sb = new StringBuilder("0123456789");
char[] chars = new char[5];
Span<char> span = new Span<char>(new char[5]);
//文字列の1番目から3文字分を、charsの0番目からコピー
sb.CopyTo(1, chars, 0, 3);
//文字列の1番目から3文字分をspanにコピー
sb.CopyTo(1, span, 3);
Console.WriteLine(chars);
Console.WriteLine(span.ToString());
123 123
Spanというのは配列などの連続したデータの一部を参照する構造体で、C#7.2から導入された機能です。
Span<char>
型のオーバーロードは.NET Core2.0から使用可能で、.NET Frameworkでは使用できません。