ここではStringBuilderクラスついて見ていきます。 StringBuilderクラスを用いることにより、直接文字列を操作するよりも効率的に文字列の加工・編集を行うことができます。

StringBuilderクラスの特徴

.NET FrameworkのStringクラスはインスタンスの持つ値を変更できないという性質を持つ不変な型です。 Stringクラスの文字列操作ではインスタンス自体が持っている値(=文字列)が変わることは無く、操作の度に結果が格納された新しい文字列のインスタンスが生成されます。

一方、StringBuilderクラスは内部にChar配列のバッファを保持し、文字列操作によってインスタンスの持つ値(=文字列)が変わる可変な型です。 StringBuilderクラスの文字列操作では、操作の際にバッファの内容のみが変更され、新しい文字列のインスタンスは生成されません。

このような特徴から、文字列の連結・挿入・削除・置換といった加工・編集を繰り返し行う場合は、StringBuilderクラスを用いて行った方が適しています。 (実際にどの程度パフォーマンスに差が出るかは§.StringBuilderのパフォーマンスで検証しています。)

一方でStringBuilderクラスにはStartsWith・IndexOf・Equalsといった文字列の探索・比較を行うメソッドは用意されていません。 探索・比較の目的にはStringクラスのメソッドRegexクラスを使用します。

StringBuilderクラスとStringクラスの違い

まずはStringBuilderクラスとStringクラスで同じ操作を行う場合の違いを見ておきます。 詳細な使い方は後述します。

Stringクラスでの文字列操作
using System;


class Sample {
  static void Main()
  {
    // インスタンスの作成
    var s = "The quick brown fox jumps over the lazy dog";

    Console.WriteLine(s);

    // 末尾の8文字を削除する
    s = s.Remove(s.Length - 8, 8);

    Console.WriteLine(s);

    // 文字列を末尾に追加する
    s = s + "silliy dog";

    Console.WriteLine(s);

    // 文字列を置換する
    s = s.Replace("The quick", "the clever");

    Console.WriteLine(s);
  }
}
Stringクラスでの文字列操作
Imports System


Class Sample
  Shared Sub Main()
    ' インスタンスの作成
    Dim s As String = "The quick brown fox jumps over the lazy dog"

    Console.WriteLine(s)

    ' 末尾の8文字を削除する
    s = s.Remove(s.Length - 8, 8)

    Console.WriteLine(s)

    ' 文字列を末尾に追加する
    s = s + "silliy dog"

    Console.WriteLine(s)

    ' 文字列を置換する
    s = s.Replace("The quick", "the clever")

    Console.WriteLine(s)
  End Sub
End Class
実行結果
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the 
The quick brown fox jumps over the silliy dog
the clever brown fox jumps over the silliy dog
StringBuilderクラスでの文字列操作
using System;
using System.Text;

class Sample {
  static void Main()
  {
    // インスタンスの作成
    var sb = new StringBuilder("The quick brown fox jumps over the lazy dog");

    Console.WriteLine(sb);

    // 末尾の8文字を削除する
    sb.Remove(sb.Length - 8, 8);

    Console.WriteLine(sb);

    // 文字列を末尾に追加する
    sb.Append("silliy dog");

    Console.WriteLine(sb);

    // 文字列を置換する
    sb.Replace("The quick", "the clever");

    Console.WriteLine(sb);
  }
}
StringBuilderクラスでの文字列操作
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    ' インスタンスの作成
    Dim sb As New StringBuilder("The quick brown fox jumps over the lazy dog")

    Console.WriteLine(sb)

    ' 末尾の8文字を削除する
    sb.Remove(sb.Length - 8, 8)

    Console.WriteLine(sb)

    ' 文字列を末尾に追加する
    sb.Append("silliy dog")

    Console.WriteLine(sb)

    ' 文字列を置換する
    sb.Replace("The quick", "the clever")

    Console.WriteLine(sb)
  End Sub
End Class
実行結果
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the 
The quick brown fox jumps over the silliy dog
the clever brown fox jumps over the silliy dog

このように、Stringクラスの文字列操作ではその都度新しいインスタンスが生成されますが、StringBuilderクラスでは単一のインスタンスを使いまわして文字列操作を繰り返すことができます。


StringBuilderクラスで加工した文字列は、最終的にStringBuilder.ToStringメソッドを使うことにより通常の文字列として取得出来ます。

StringBuilderからStringへの変換
using System;
using System.Text;

class Sample {
  static void Main()
  {
    var sb = new StringBuilder("The quick brown fox jumps");

    sb.Append(" over the lazy dog");

    // StringBuilderクラスが保持している文字列をStringとして取得する
    var s = sb.ToString();

    Console.WriteLine(s);
  }
}
StringBuilderからStringへの変換
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    Dim sb As New StringBuilder("The quick brown fox jumps")

    sb.Append(" over the lazy dog")

    ' StringBuilderクラスが保持している文字列をStringとして取得する
    Dim s As String = sb.ToString()

    Console.WriteLine(s)
  End Sub
End Class
実行結果
The quick brown fox jumps over the lazy dog

StringBuilderによる文字列操作

文字列の追加 (Append, AppendLine)

StringBuilder.Appendメソッドは、バッファの末尾に文字列を追加するメソッドで、String.Concatメソッドや加算演算子+による文字列の連結操作に相当します。

また、StringBuilder.AppendLineメソッドは、バッファの末尾に文字列を追加する点はAppendメソッドと同様ですが、自動的に改行文字も追加されます。 AppendLineメソッドに引数を指定しなかった場合は、改行文字のみが追加されます。 (Appendメソッド・AppendLineメソッドはConsole.Writeメソッド・WriteLineメソッドと似た動作と考えてよいでしょう)

StringBuilder.AppendLine・Appendメソッドを使って改行付き・改行なしで文字列を追加する
using System;
using System.Text;

class Sample {
  static void Main()
  {
    var sb = new StringBuilder(); // 空のStringBuilderを作成

    Console.WriteLine("<{0}>", sb);

    // 文字列を追加
    sb.Append("The quick brown fox jumps");

    Console.WriteLine("<{0}>", sb);

    // 改行文字を付けて文字列を追加
    sb.AppendLine(" over the lazy dog");

    Console.WriteLine("<{0}>", sb);

    // 文字列を追加
    sb.Append("The quick brown fox jumps");

    Console.WriteLine("<{0}>", sb);

    // 改行文字のみを追加
    sb.AppendLine();

    Console.WriteLine("<{0}>", sb);
  }
}
StringBuilder.AppendLine・Appendメソッドを使って改行付き・改行なしで文字列を追加する
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    Dim sb As New StringBuilder() ' 空のStringBuilderを作成

    Console.WriteLine("<{0}>", sb)

    ' 文字列を追加
    sb.Append("The quick brown fox jumps")

    Console.WriteLine("<{0}>", sb)

    ' 改行文字を付けて文字列を追加
    sb.AppendLine(" over the lazy dog")

    Console.WriteLine("<{0}>", sb)

    ' 文字列を追加
    sb.Append("The quick brown fox jumps")

    Console.WriteLine("<{0}>", sb)

    ' 改行文字のみを追加
    sb.AppendLine()

    Console.WriteLine("<{0}>", sb)
  End Sub
End Class
実行結果
<>
<The quick brown fox jumps>
<The quick brown fox jumps over the lazy dog
>
<The quick brown fox jumps over the lazy dog
The quick brown fox jumps>
<The quick brown fox jumps over the lazy dog
The quick brown fox jumps
>

AppendLineメソッドでは常にEnvironment.NewLineが改行文字として使われます。 異なる改行文字を使用する方法については§.StringBuilderと改行文字で後述します。

StringBuilderと改行文字

AppendLineメソッドでは、常にEnvironment.NewLineが改行文字として使用されます。 そのため、実行環境によって改行文字が変わると不都合がある困る場合や、CRまたはLFのみを追加したいといった場合は、AppendLineメソッドの代わりにAppendメソッドを使い、個別に改行文字を書き込む必要があります。

StringBuilderでEnvironment.NewLine以外の改行文字を追加する
using System;
using System.Text;

class Sample {
  static void Main()
  {
    var sb = new StringBuilder();

    sb.Append("The quick brown fox jumps over the lazy dog");
    sb.AppendLine(); // 現在の環境での改行文字を追加

    sb.Append("The quick brown fox jumps over the lazy dog");
    sb.Append("\r"); // CRを追加

    sb.Append("The quick brown fox jumps over the lazy dog");
    sb.Append("\n"); // LFを追加

    sb.Append("The quick brown fox jumps over the lazy dog");
    sb.Append("\r\n"); // CRLFを追加

    // わかりやすさのために改行文字をエスケープして表示
    Console.WriteLine(sb.Replace("\r", "\\r").Replace("\n", "\\n"));
  }
}
StringBuilderでEnvironment.NewLine以外の改行文字を追加する
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    Dim sb As New StringBuilder()

    sb.Append("The quick brown fox jumps over the lazy dog")
    sb.AppendLine() ' 現在の環境での改行文字を追加

    sb.Append("The quick brown fox jumps over the lazy dog")
    sb.Append(ControlChars.Cr) ' CRを追加

    sb.Append("The quick brown fox jumps over the lazy dog")
    sb.Append(ControlChars.Lf) ' LFを追加

    sb.Append("The quick brown fox jumps over the lazy dog")
    sb.Append(ControlChars.CrLf) ' CRLFを追加

    ' わかりやすさのために改行文字をエスケープして表示
    Console.WriteLine(sb.Replace(ControlChars.Cr, "\r").Replace(ControlChars.Lf, "\n"))
  End Sub
End Class
実行結果(Windows環境での場合)
The quick brown fox jumps over the lazy dog\r\n
The quick brown fox jumps over the lazy dog\r
The quick brown fox jumps over the lazy dog\n
The quick brown fox jumps over the lazy dog\r\n

上記の実行結果ではわかりやすさのために出力結果に改行を加えています。


これとは別に、StringWriterクラスを使うことによっても、StringBuilderで任意の改行文字を使うようにすることができます。 StreamWriter.NewLineプロパティを変更すると、書き込まれる改行文字を変更することができます。 次の例では、結果が分かりやすいように改行文字を"↵\r\n"に変更しています。 もちろん、改行文字にCRまたはLFのみを指定することもできます。

StringBuilderとStringWriterを使ってEnvironment.NewLine以外の改行文字を追加する
using System;
using System.IO;
using System.Text;

class Sample {
  static void Main()
  {
    var sb = new StringBuilder(); // 空のStringBuilderを作成

    // StringBuilderに文字列を書き込むためのStringWriterを作成
    using (StringWriter writer = new StringWriter(sb)) {
      // 改行文字を変更する
      writer.NewLine = "↵\r\n";

      // 改行文字を付けて文字列を書き込む
      writer.WriteLine("The quick brown fox jumps");
      writer.WriteLine("over the lazy dog");

      // 改行文字のみを書き込む
      writer.WriteLine();

      // 数値を書き込む
      writer.WriteLine(16);
      writer.WriteLine(Math.PI);
    }

    Console.WriteLine(sb);
  }
}
StringBuilderとStringWriterを使ってEnvironment.NewLine以外の改行文字を追加する
Imports System
Imports System.IO
Imports System.Text

Class Sample
  Shared Sub Main()
    Dim sb As New StringBuilder() ' 空のStringBuilderを作成

    ' StringBuilderに文字列を書き込むためのStringWriterを作成
    Using writer As New StringWriter(sb)
      ' 改行文字を変更する
      writer.NewLine = "↵" + vbCrLf

      ' 改行文字を付けて文字列を書き込む
      writer.WriteLine("The quick brown fox jumps")
      writer.WriteLine("over the lazy dog")

      ' 改行文字のみを書き込む
      writer.WriteLine()

      ' 数値を書き込む
      writer.WriteLine(16)
      writer.WriteLine(Math.PI)
    End Using

    Console.WriteLine(sb)
  End Sub
End Class
実行結果
The quick brown fox jumps↵
over the lazy dog↵
↵
16↵
3.14159265358979↵

StringWriterクラスについて詳しくはStringReaderクラス/StringWriterクラスをご覧ください。

書式を指定した文字列の追加 (AppendFormat)

StringBuilder.AppendFormatメソッドは、指定された書式に整形してからバッファに追加するメソッドです。 String.Formatメソッドによる書式化と文字列の連結を同時に行う操作に相当します。

AppendFormatメソッドでは、String.FormatメソッドやConsole.WriteLineメソッドなどと同様に書式指定子を指定することができます。

StringBuilder.AppendFormatメソッドを使って指定した書式で文字列化して追加する
using System;
using System.Text;

class Sample {
  static void Main()
  {
    var sb = new StringBuilder(); // 空のStringBuilderを作成

    // 書式を指定し、数値72を文字列化して追加
    sb.AppendFormat("{0:D} {0:D4} {0:N4}", 72);

    Console.WriteLine(sb);
  }
}
StringBuilder.AppendFormatメソッドを使って指定した書式で文字列化して追加する
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    Dim sb As New StringBuilder() ' 空のStringBuilderを作成

    ' 書式を指定し、数値72を文字列化して追加
    sb.AppendFormat("{0:D} {0:D4} {0:N4}", 72)

    Console.WriteLine(sb)
  End Sub
End Class
実行結果
72 0072 72.0000

AppendFormatメソッドでは改行文字は追加されません。 AppendFormatLineのようなメソッドもないので、改行文字を追加したい場合はAppendLineメソッドと組み合わせて使う必要があります。

書式化について詳しくは文字列と書式を参照してください。

AppendFormatメソッドでは任意の型の引数を書式化して追加することが出来ますが、Appendメソッドも同様に文字列以外の値を追加することが出来ます。 この際、引数として指定された値は既定の書式で自動的に文字列化されてから追加されます。

StringBuilder.Appendメソッドで文字列型以外の値を追加する
using System;
using System.Text;

class Sample {
  static void Main()
  {
    var sb = new StringBuilder(); // 空のStringBuilderを作成

    Console.WriteLine("<{0}>", sb);

    // int型の数値を追加
    sb.Append(16);
    sb.AppendLine();

    Console.WriteLine("<{0}>", sb);

    // double型の数値を追加
    sb.Append(Math.PI);
    sb.AppendLine();

    Console.WriteLine("<{0}>", sb);

    // bool型の値を追加
    sb.Append(true);
    sb.Append(false);
    sb.AppendLine();

    Console.WriteLine("<{0}>", sb);
  }
}
StringBuilder.Appendメソッドで文字列型以外の値を追加する
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    Dim sb As New StringBuilder() ' 空のStringBuilderを作成

    Console.WriteLine("<{0}>", sb)

    ' Integer型の数値を追加
    sb.Append(16)
    sb.AppendLine()

    Console.WriteLine("<{0}>", sb)

    ' Double型の数値を追加
    sb.Append(Math.PI)
    sb.AppendLine()

    Console.WriteLine("<{0}>", sb)

    ' Boolean型の値を追加
    sb.Append(true)
    sb.Append(false)
    sb.AppendLine()

    Console.WriteLine("<{0}>", sb)
  End Sub
End Class
実行結果
<>
<16
>
<16
3.14159265358979
>
<16
3.14159265358979
TrueFalse
>

文字列の挿入・削除・置換 (Insert, Remove, Replace)

Stringクラスと同様、文字列の挿入・削除・置換にはStringBuilder.InsertStringBuilder.RemoveStringBuilder.Replaceの各メソッドが使えます。 引数と動作はStringクラスのものとほぼ同じですが、InsertメソッドはAppendメソッド同様に文字列以外も追加出来ます。 なお、String.Removeメソッドとは異なり、StringBuilder.Removeメソッドでは削除する文字数を省略することは出来ません。

StringBuilder.Insert・Remove・Replaceメソッドを使って文字列を挿入・削除・置換する
using System;
using System.Text;

class Sample {
  static void Main()
  {
    var sb = new StringBuilder("The quick brown fox jumps over the lazy dog");

    Console.WriteLine("<{0}>", sb);

    // 4文字目から6文字分を削除
    sb.Remove(4, 6);

    Console.WriteLine("<{0}>", sb);

    // 29文字目から4文字分を削除
    sb.Remove(29, 4);

    Console.WriteLine("<{0}>", sb);

    // 29文字目に数値3を追加
    sb.Insert(29, 3);

    Console.WriteLine("<{0}>", sb);

    // 30文字目に" white "を追加
    sb.Insert(30, " white");

    Console.WriteLine("<{0}>", sb);

    // 文字列"dog"を"dogs"に置換
    sb.Replace("dog", "dogs");

    Console.WriteLine("<{0}>", sb);

    // 文字'o'を'a'に置換
    sb.Replace('o', 'a');

    Console.WriteLine("<{0}>", sb);

    // 文字列"brawn"をnullに置換(=削除)
    sb.Replace("brawn ", null);

    Console.WriteLine("<{0}>", sb);
  }
}
StringBuilder.Insert・Remove・Replaceメソッドを使って文字列を挿入・削除・置換する
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    Dim sb As New StringBuilder("The quick brown fox jumps over the lazy dog")

    Console.WriteLine("<{0}>", sb)

    ' 4文字目から6文字分を削除
    sb.Remove(4, 6)

    Console.WriteLine("<{0}>", sb)

    ' 29文字目から4文字分を削除
    sb.Remove(29, 4)

    Console.WriteLine("<{0}>", sb)

    ' 29文字目に数値3を追加
    sb.Insert(29, 3)

    Console.WriteLine("<{0}>", sb)

    ' 30文字目に" white "を追加
    sb.Insert(30, " white")

    Console.WriteLine("<{0}>", sb)

    ' 文字列"dog"を"dogs"に置換
    sb.Replace("dog", "dogs")

    Console.WriteLine("<{0}>", sb)

    ' 文字'o'を'a'に置換
    sb.Replace("o"c, "a"c)

    Console.WriteLine("<{0}>", sb)

    ' 文字列"brawn"をNothingに置換(=削除)
    sb.Replace("brawn ", Nothing)

    Console.WriteLine("<{0}>", sb)
  End Sub
End Class
実行結果
<The quick brown fox jumps over the lazy dog>
<The brown fox jumps over the lazy dog>
<The brown fox jumps over the  dog>
<The brown fox jumps over the 3 dog>
<The brown fox jumps over the 3 white dog>
<The brown fox jumps over the 3 white dogs>
<The brawn fax jumps aver the 3 white dags>
<The fax jumps aver the 3 white dags>

StringBuilderの内容を完全に削除したい場合は、Clearメソッドを使うこともできます。

メソッドチェイン

Append, AppendLine, AppendFormat, Insert, Remove, Replaceの各メソッドは、戻り値としてインスタンス自身を返します。 これにより、stringクラスと同様にメソッドチェインを記述出来ます。

StringとStringBuilderで、メソッドチェインによって文字列操作を連結する
using System;
using System.Text;

class Sample {
  static void Main()
  {
    var s = "The quick brown fox jumps over the lazy dog";
    var sb = new StringBuilder("The quick brown fox jumps over the lazy dog");

    Console.WriteLine("String       : <{0}>", s);
    Console.WriteLine("StringBuilder: <{0}>", sb);

    s  =  s.Remove(4, 6).Remove(29, 4).Insert(29, "3").Insert(30, " white").Replace("dog", "dogs").Replace('o', 'a').Replace("brawn ", null);
    sb = sb.Remove(4, 6).Remove(29, 4).Insert(29,  3 ).Insert(30, " white").Replace("dog", "dogs").Replace('o', 'a').Replace("brawn ", null);

    Console.WriteLine("String       : <{0}>", s);
    Console.WriteLine("StringBuilder: <{0}>", sb);
  }
}
StringとStringBuilderで、メソッドチェインによって文字列操作を連結する
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    Dim s As String = "The quick brown fox jumps over the lazy dog"
    Dim sb As New StringBuilder("The quick brown fox jumps over the lazy dog")

    Console.WriteLine("String       : <{0}>", s)
    Console.WriteLine("StringBuilder: <{0}>", sb)

    s  =  s.Remove(4, 6).Remove(29, 4).Insert(29, "3").Insert(30, " white").Replace("dog", "dogs").Replace("o"c, "a"c).Replace("brawn ", Nothing)
    sb = sb.Remove(4, 6).Remove(29, 4).Insert(29,  3 ).Insert(30, " white").Replace("dog", "dogs").Replace("o"c, "a"c).Replace("brawn ", Nothing)

    Console.WriteLine("String       : <{0}>", s)
    Console.WriteLine("StringBuilder: <{0}>", sb)
  End Sub
End Class
実行結果
String       : <The quick brown fox jumps over the lazy dog>
StringBuilder: <The quick brown fox jumps over the lazy dog>
String       : <The fax jumps aver the 3 white dags>
StringBuilder: <The fax jumps aver the 3 white dags>

バッファの操作

インスタンスの内容が不変であるStringクラスとは異なり、内容が可変であるStringBuilderクラスでは内部にサイズが変更可能なバッファを備えています。 ここではStringBuilderのバッファの操作を行うプロパティ・メソッドについて見ていきます。

文字列の長さとバッファのクリア (Length, Clear)

StringBuilderに格納されている文字数はLengthプロパティで取得できます。 String.Lengthプロパティとは異なり、StringBuilderではLengthプロパティを設定することによって文字列の長さを変更することができます。 また、Lengthプロパティを0にすることにより、バッファの内容をクリアすることが出来ます。

Lengthを現在の長さより小さくすると文字列が切り詰められ、逆に大きくした場合はヌル文字(U+0000)で埋められます。

StringBuilder.Lengthプロパティを使って文字列の長さを変更する
using System;
using System.Text;

class Sample {
  static void Main()
  {
    var sb = new StringBuilder("The quick brown fox jumps over the lazy dog");

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length);

    // 長さを18文字分短くする
    sb.Length -= 18;

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length);

    // 長さを0にする(バッファをクリアする)
    sb.Length = 0;

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length);

    // 文字列を追加
    sb.Append("The quick brown fox");

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length);

    // 長さを30にする
    sb.Length = 30;

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length);

    // ヌル文字を'.'に置換
    sb.Replace('\0', '.');

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length);
  }
}
StringBuilder.Lengthプロパティを使って文字列の長さを変更する
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    Dim sb As New StringBuilder("The quick brown fox jumps over the lazy dog")

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length)

    ' 長さを18文字分短くする
    sb.Length -= 18

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length)

    ' 長さを0にする(バッファをクリアする)
    sb.Length = 0

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length)

    ' 文字列を追加
    sb.Append("The quick brown fox")

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length)

    ' 長さを30にする
    sb.Length = 30

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length)

    ' ヌル文字を'.'に置換
    sb.Replace(vbNullChar, "."c)

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length)
  End Sub
End Class
実行結果
<The quick brown fox jumps over the lazy dog> (Length=43)
<The quick brown fox jumps> (Length=25)
<> (Length=0)
<The quick brown fox> (Length=19)
<The quick brown fox> (Length=30)
<The quick brown fox...........> (Length=30)

.NET Framework 4からは、バッファのクリアにStringBuilder.Clearメソッドを使うことが出来ます。 結果はLengthに0を指定した場合と同じです。

StringBuilder.Clearメソッドを使ってStringBuilderの内容をクリアする
using System;
using System.Text;

class Sample {
  static void Main()
  {
    var sb = new StringBuilder("The quick brown fox jumps over the lazy dog");

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length);

    // Clearメソッドでバッファをクリアする
    sb.Clear();

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length);

    // 文字列を追加
    sb.Append("The quick brown fox");

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length);

    // 長さを0にする (Clearメソッドと同じ操作となる)
    sb.Length = 0;

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length);

    // 文字列を追加
    sb.Append("The quick brown fox");

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length);
  }
}
StringBuilder.Clearメソッドを使ってStringBuilderの内容をクリアする
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    Dim sb As New StringBuilder("The quick brown fox jumps over the lazy dog")

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length)

    ' Clearメソッドでバッファをクリアする
    sb.Clear()

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length)

    ' 文字列を追加
    sb.Append("The quick brown fox")

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length)

    ' 長さを0にする (Clearメソッドと同じ操作となる)
    sb.Length = 0

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length)

    ' 文字列を追加
    sb.Append("The quick brown fox")

    Console.WriteLine("<{0}> (Length={1})", sb, sb.Length)
  End Sub
End Class
実行結果
<The quick brown fox jumps over the lazy dog> (Length=43)
<> (Length=0)
<The quick brown fox> (Length=19)
<> (Length=0)
<The quick brown fox> (Length=19)

バッファの容量 (Capacity, EnsureCapacity)

StringBuilderクラスでは、常に文字列の実際の長さ以上のバッファが確保されています。 Capacityプロパティは現在のバッファのサイズ(容量)を取得するためのプロパティです。 このプロパティを設定することでバッファのサイズを変更することもできます。 ただし、当然現在の文字列の長さ未満にすることはできません。 この場合、例外ArgumentOutOfRangeExceptionがスローされます。

また、EnsureCapacityメソッドでもバッファのサイズを変更することができます。 このメソッドは、容量を減らすためではなく、現在の容量よりも多く確保したい場合に使用します。 EnsureCapacityメソッドでバッファを確保する場合、実際に確保される容量が指定した容量よりも大きくなる場合があります。 つまり、 最小でもメソッドで指定したサイズが確保されます。

StringBuilderのコンストラクタでも容量の初期値を指定してインスタンスを生成することができます(指定しない場合は初期値16でバッファが確保されます)。 StringBuilderに追加しようとする文字列の量をあらかじめ予測出来る場合は、あらかじめ適切な容量のバッファを確保しておくことでバッファ確保によるオーバーヘッドを減らすことが出来ます。

StringBuilderでバッファの容量を取得・変更する
using System;
using System.Text;

class Sample {
  static void Main()
  {
    // 容量8のバッファを確保してインスタンスを作成
    var sb = new StringBuilder(8);

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity);

    // バッファのサイズを超える長さの文字列を追加
    sb.Append("The quick brown fox");

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity);

    // 容量を現在の長さの3/2(1.5倍)にする
    sb.Capacity = (sb.Length * 3) / 2;

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity);

    // 文字列を追加
    sb.Append(" jumps over the lazy dog");

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity);

    // 容量を現在の長さまで減らす
    sb.Capacity = sb.Length;

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity);

    // 最小で100の容量を確保する
    sb.EnsureCapacity(100);

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity);

    // 文字列を追加
    sb.Append(" The quick brown fox jumps over the lazy dog");

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity);
  }
}
StringBuilderでバッファの容量を取得・変更する
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    ' 容量8のバッファを確保してインスタンスを作成
    Dim sb As New StringBuilder(8)

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity)

    ' バッファのサイズを超える長さの文字列を追加
    sb.Append("The quick brown fox")

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity)

    ' 容量を現在の長さの3/2(1.5倍)にする
    sb.Capacity = (sb.Length * 3) \ 2

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity)

    ' 文字列を追加
    sb.Append(" jumps over the lazy dog")

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity)

    ' 容量を現在の長さまで減らす
    sb.Capacity = sb.Length

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity)

    ' 最小で100の容量を確保する
    sb.EnsureCapacity(100)

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity)

    ' 文字列を追加
    sb.Append(" The quick brown fox jumps over the lazy dog")

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity)
  End Sub
End Class
実行結果
(Length=0, Capacity=8) <>
(Length=19, Capacity=19) <The quick brown fox>
(Length=19, Capacity=38) <The quick brown fox>
(Length=43, Capacity=43) <The quick brown fox jumps over the lazy dog>
(Length=43, Capacity=43) <The quick brown fox jumps over the lazy dog>
(Length=43, Capacity=100) <The quick brown fox jumps over the lazy dog>
(Length=87, Capacity=100) <The quick brown fox jumps over the lazy dog The quick brown fox jumps over the lazy dog>

バッファの最大容量 (MaxCapacity)

StringBuilderが確保するバッファの最大容量を指定することも出来ます。 最大容量を指定するには、コンストラクタで指定する必要があります。 特に指定しない場合は初期値Int32.MaxValue(2,147,483,647)が最大容量に指定されます。 インスタンスの最大容量はMaxCapacityプロパティで取得することが出来ます。

文字列を追加しようとしたときやバッファのサイズを変更しようとしたときなど、確保しようとしているバッファの容量が最大容量を超える場合は例外ArgumentOutOfRangeExceptionがスローされます。

StringBuilderのバッファの最大容量を指定する
using System;
using System.Text;

class Sample {
  static void Main()
  {
    // 容量24のバッファを確保し、最大容量に32を指定してインスタンスを作成
    var sb = new StringBuilder(24, 32);

    Console.WriteLine("MaxCapacity: {0}", sb.MaxCapacity);
    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity);

    // 文字列を追加
    sb.Append("The quick brown fox");

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity);

    // 追加することで最大容量を超える長さの文字列を追加
    try {
      sb.Append(" jumps over the lazy dog");
    }
    catch (ArgumentException) {
      Console.WriteLine("ArgumentException!!");
    }

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity);
  }
}
StringBuilderのバッファの最大容量を指定する
Imports System
Imports System.Text

Class Sample
  Shared Sub Main()
    ' 容量24のバッファを確保し、最大容量に32を指定してインスタンスを作成
    Dim sb As New StringBuilder(24, 32)

    Console.WriteLine("MaxCapacity: {0}", sb.MaxCapacity)
    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity)

    ' 文字列を追加
    sb.Append("The quick brown fox")

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity)

    ' 追加することで最大容量を超える長さの文字列を追加
    Try
      sb.Append(" jumps over the lazy dog")
    Catch ex As ArgumentException
      Console.WriteLine("ArgumentException!!")
    End Try

    Console.WriteLine("(Length={1}, Capacity={2}) <{0}>", sb, sb.Length, sb.Capacity)
  End Sub
End Class
実行結果
MaxCapacity: 32
(Length=0, Capacity=24) <>
(Length=19, Capacity=24) <The quick brown fox>
ArgumentException!!
(Length=19, Capacity=19) <The quick brown fox>

StringBuilderのパフォーマンス

StringクラスとStringBuilderクラスの最大の違いは、文字列操作の都度インスタンスが生成されるかどうかですが、これによりパフォーマンスにも違いが現れます。 StringクラスとStringBuilderクラスを用いて文字列の追加を行い、実行時間にどれくらいの差があるかを比較してみます。 次のコードでは、文字列の追加を10,000回行っています。 あらかじめStringBuilderの容量を指定している点にも注目してください。

検証に使ったコード
using System;
using System.Diagnostics;
using System.Text;

class Sample {
  static void Main()
  {
    const int repeat = 10 * 1000;
    const string text = "The quick brown fox jumps over the lazy dog";

    for (var t = 0; t < 3; t++) {
      // Stringクラス
      Stopwatch sw1 = Stopwatch.StartNew();
      string s = string.Empty;

      for (var i = 0; i < repeat; i++) {
        s = s + text;
      }

      sw1.Stop();

      // StringBuilderクラス
      Stopwatch sw2 = Stopwatch.StartNew();
      StringBuilder sb = new StringBuilder(repeat * text.Length);

      for (var i = 0; i < repeat; i++) {
        sb.Append(text);
      }

      sw2.Stop();

      Console.WriteLine(sw1.Elapsed);
      Console.WriteLine(sw2.Elapsed);
      Console.WriteLine("{0:P2}", (double)sw2.ElapsedTicks / (double)sw1.ElapsedTicks);
    }
  }
}
実行結果の一例
00:00:10.8776387
00:00:00.0066486
0.06%
00:00:11.2358833
00:00:00.0066315
0.06%
00:00:11.2715232
00:00:00.0066818
0.06%

結果のとおり、実行時間に大幅な差があります。


続いて、文字列の置換で比較してみます。 次のコードでは文字列の置換を10,000回行っています。 文字列の中間にある文字を置換し、かつ置換した結果文字列が長くなるようにしています。

検証に使ったコード
using System;
using System.Diagnostics;
using System.Text;

class Sample {
  static void Main()
  {
    const int repeat = 10 * 1000;

    for (var t = 0; t < 3; t++) {
      // Stringクラス
      Stopwatch sw1 = Stopwatch.StartNew();
      string s = "foobarbaz";

      for (var i = 0; i < repeat; i++) {
        s = s.Replace("bar", "foobarbaz");
      }

      sw1.Stop();

      // StringBuilderクラス
      Stopwatch sw2 = Stopwatch.StartNew();
      StringBuilder sb = new StringBuilder("foobarbaz", 7 * repeat);

      for (var i = 0; i < repeat; i++) {
        sb.Replace("bar", "foobarbaz");
      }

      sw2.Stop();

      Console.WriteLine(sw1.Elapsed);
      Console.WriteLine(sw2.Elapsed);
      Console.WriteLine("{0:P2}", (double)sw2.ElapsedTicks / (double)sw1.ElapsedTicks);
    }
  }
}
実行結果の一例
00:00:03.3487843
00:00:02.0426295
61.00%
00:00:03.2005363
00:00:01.9556713
61.10%
00:00:03.3387661
00:00:01.8794274
56.29%

先の例ほどの差にはなりませんが、それでもStringBuilderを用いたほうがStringよりも実行時間が短くなっています。 このように文字列操作を繰り返し行う場合は、StringBuilderを使うことでより短い時間で処理を終わらせることが期待できます。