次の方法で共有


System.Text.StringBuilder クラス

この記事では、この API のリファレンス ドキュメントへの補足的な解説を提供します。

StringBuilder クラスは、値が変更可能な文字シーケンスである文字列のようなオブジェクトを表します。

StringBuilder と String 型

StringBuilderStringはどちらも文字のシーケンスを表しますが、実装方法は異なります。 String は変更できない型です。 つまり、 String オブジェクトを変更するように見える各操作は、実際には新しい文字列を作成します。

たとえば、次の C# の例の String.Concat メソッドの呼び出しは、 valueという名前の文字列変数の値を変更するように見えます。 実際、Concat メソッドは、メソッドに渡されたvalue オブジェクトとは異なる値とアドレスを持つvalue オブジェクトを返します。 この例は、 /unsafe コンパイラ オプションを使用してコンパイルする必要があることに注意してください。

using System;

public class Example7
{
    public unsafe static void Main()
    {
        string value = "This is the first sentence" + ".";
        fixed (char* start = value)
        {
            value = String.Concat(value, "This is the second sentence. ");
            fixed (char* current = value)
            {
                Console.WriteLine(start == current);
            }
        }
    }
}
// The example displays the following output:
//      False
    let mutable value = "This is the first sentence" + "."
    use start = fixed value
    value <- System.String.Concat(value, "This is the second sentence. ")
    use current = fixed value
    printfn $"{start = current}"
// The example displays the following output:
//      False

広範な文字列操作を実行するルーチン (ループ内で文字列を何度も変更するアプリなど) の場合、文字列を繰り返し変更すると、パフォーマンスが大幅に低下する可能性があります。 別の方法は、変更可能な文字列クラスである StringBuilderを使用することです。 変更可能性とは、クラスのインスタンスが作成されたら、文字の追加、削除、置換、または挿入によって変更できることを意味します。

重要

一般に、 StringBuilder クラスは String クラスよりもパフォーマンスが向上しますが、文字列を操作するときは常に StringStringBuilder に自動的に置き換えるべきではありません。 パフォーマンスは、文字列のサイズ、新しい文字列に割り当てられるメモリの量、コードが実行されているシステム、操作の種類によって異なります。 コードをテストして、実際にパフォーマンスが大幅に向上 StringBuilder かどうかを判断する準備をしておく必要があります。

次の条件下で、 String クラスを使用することを検討してください。

  • コードが文字列に加える変更の数が少ない場合。 このような場合、 StringBuilder は、 Stringに対してごくわずかまたはまったくパフォーマンス向上を提供しない可能性があります。
  • 固定数の連結操作 (特に文字列リテラルを使用) を実行する場合。 この場合、コンパイラは連結操作を 1 つの操作に結合する可能性があります。
  • 文字列の構築中に広範な検索操作を実行する必要がある場合。 StringBuilder クラスには、IndexOfStartsWithなどの検索メソッドがありません。 StringBuilder オブジェクトをこれらの操作のStringに変換する必要があります。これにより、StringBuilderを使用するとパフォーマンス上のメリットが無効になる可能性があります。 詳細については、「 StringBuilder オブジェクト内のテキストを検索する 」セクションを参照してください。

次の条件下で、 StringBuilder クラスを使用することを検討してください。

  • コードがデザイン時に文字列に対して不明な数の変更を行うと予想される場合 (たとえば、ループを使用して、ユーザー入力を含む文字列のランダムな数を連結する場合)。
  • コードが文字列に大量の変更を加えると予想される場合。

StringBuilder のしくみ

StringBuilder.Length プロパティは、StringBuilder オブジェクトに現在含まれている文字数を示します。 StringBuilder オブジェクトに文字を追加すると、オブジェクトに含めることができる文字数を定義するStringBuilder.Capacity プロパティのサイズと等しくなるまで、その長さが長くなります。 追加された文字数によって、 StringBuilder オブジェクトの長さが現在の容量を超える場合、新しいメモリが割り当てられ、 Capacity プロパティの値が 2 倍になり、新しい文字が StringBuilder オブジェクトに追加され、 Length プロパティが調整されます。 StringBuilder オブジェクトの追加メモリは、StringBuilder.MaxCapacity プロパティで定義された値に達するまで動的に割り当てられます。 最大容量に達すると、 StringBuilder オブジェクトにこれ以上メモリを割り当てず、文字を追加したり、最大容量を超えて拡張しようとすると、 ArgumentOutOfRangeException または OutOfMemoryException 例外がスローされます。

次の例は、 StringBuilder オブジェクトが新しいメモリを割り当て、オブジェクトに割り当てられた文字列が拡張されると容量を動的に増やす方法を示しています。 このコードでは、既定の (パラメーターなしの) コンストラクターを呼び出して、 StringBuilder オブジェクトを作成します。 このオブジェクトの既定の容量は 16 文字で、最大容量は 20 億文字を超えています。 文字列 "This is a sentence" を追加すると、文字列の長さ (19 文字) が StringBuilder オブジェクトの既定の容量を超えるため、新しいメモリ割り当てが行われます。 オブジェクトの容量は 32 文字になり、新しい文字列が追加され、オブジェクトの長さが 19 文字になりました。 次に、このコードは文字列 "This is a additional sentence." を StringBuilder オブジェクトの値に 11 回追加します。 追加操作によって StringBuilder オブジェクトの長さが容量を超えるたびに、既存の容量が 2 倍になり、 Append 操作が成功します。

using System;
using System.Reflection;
using System.Text;

public class Example4
{
    public static void Main()
    {
        StringBuilder sb = new StringBuilder();
        ShowSBInfo(sb);
        sb.Append("This is a sentence.");
        ShowSBInfo(sb);
        for (int ctr = 0; ctr <= 10; ctr++)
        {
            sb.Append("This is an additional sentence.");
            ShowSBInfo(sb);
        }
    }

    private static void ShowSBInfo(StringBuilder sb)
    {
        foreach (var prop in sb.GetType().GetProperties())
        {
            if (prop.GetIndexParameters().Length == 0)
                Console.Write("{0}: {1:N0}    ", prop.Name, prop.GetValue(sb));
        }
        Console.WriteLine();
    }
}
// The example displays the following output:
//    Capacity: 16    MaxCapacity: 2,147,483,647    Length: 0
//    Capacity: 32    MaxCapacity: 2,147,483,647    Length: 19
//    Capacity: 64    MaxCapacity: 2,147,483,647    Length: 50
//    Capacity: 128    MaxCapacity: 2,147,483,647    Length: 81
//    Capacity: 128    MaxCapacity: 2,147,483,647    Length: 112
//    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 143
//    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 174
//    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 205
//    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 236
//    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 267
//    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 298
//    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 329
//    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 360
open System.Text

let showSBInfo (sb: StringBuilder) =
    for prop in sb.GetType().GetProperties() do
        if prop.GetIndexParameters().Length = 0 then
            printf $"{prop.Name}: {prop.GetValue sb:N0}    "

    printfn ""

let sb = StringBuilder()
showSBInfo sb
sb.Append "This is a sentence." |> ignore
showSBInfo sb

for i = 0 to 10 do
    sb.Append "This is an additional sentence." |> ignore
    showSBInfo sb

// The example displays the following output:
//    Capacity: 16    MaxCapacity: 2,147,483,647    Length: 0
//    Capacity: 32    MaxCapacity: 2,147,483,647    Length: 19
//    Capacity: 64    MaxCapacity: 2,147,483,647    Length: 50
//    Capacity: 128    MaxCapacity: 2,147,483,647    Length: 81
//    Capacity: 128    MaxCapacity: 2,147,483,647    Length: 112
//    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 143
//    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 174
//    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 205
//    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 236
//    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 267
//    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 298
//    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 329
//    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 360
Imports System.Reflection
Imports System.Text

Module Example5
    Public Sub Main()
        Dim sb As New StringBuilder()
        ShowSBInfo(sb)
        sb.Append("This is a sentence.")
        ShowSBInfo(sb)
        For ctr As Integer = 0 To 10
            sb.Append("This is an additional sentence.")
            ShowSBInfo(sb)
        Next
    End Sub

    Public Sub ShowSBInfo(sb As StringBuilder)
        For Each prop In sb.GetType().GetProperties
            If prop.GetIndexParameters().Length = 0 Then
                Console.Write("{0}: {1:N0}    ", prop.Name, prop.GetValue(sb))
            End If
        Next
        Console.WriteLine()
    End Sub
End Module
' The example displays the following output:
'    Capacity: 16    MaxCapacity: 2,147,483,647    Length: 0
'    Capacity: 32    MaxCapacity: 2,147,483,647    Length: 19
'    Capacity: 64    MaxCapacity: 2,147,483,647    Length: 50
'    Capacity: 128    MaxCapacity: 2,147,483,647    Length: 81
'    Capacity: 128    MaxCapacity: 2,147,483,647    Length: 112
'    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 143
'    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 174
'    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 205
'    Capacity: 256    MaxCapacity: 2,147,483,647    Length: 236
'    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 267
'    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 298
'    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 329
'    Capacity: 512    MaxCapacity: 2,147,483,647    Length: 360

メモリ割り当て

StringBuilder オブジェクトの既定の容量は 16 文字で、既定の最大容量はInt32.MaxValue。 これらの既定値は、 StringBuilder() コンストラクターと StringBuilder(String) コンストラクターを呼び出す場合に使用されます。

StringBuilder オブジェクトの初期容量は、次の方法で明示的に定義できます。

  • オブジェクトを作成するときに、capacity パラメーターを含むStringBuilderコンストラクターのいずれかを呼び出します。
  • StringBuilder.Capacity プロパティに新しい値を明示的に割り当てて、既存のStringBuilder オブジェクトを展開します。 (新しい容量が既存の容量より小さいか、 StringBuilder オブジェクトの最大容量より大きい場合、プロパティは例外をスローします)。
  • 新しい容量で StringBuilder.EnsureCapacity メソッドを呼び出すこと。 新しい容量は、 StringBuilder オブジェクトの最大容量を超えてはなりません。 ただし、 Capacity プロパティへの割り当てとは異なり、必要な新しい容量が既存の容量より小さい場合、 EnsureCapacity は例外をスローしません。 この場合、メソッド呼び出しは無効です。

コンストラクター呼び出しで StringBuilder オブジェクトに割り当てられた文字列の長さが既定の容量または指定した容量を超えた場合、 Capacity プロパティは、 value パラメーターで指定された文字列の長さに設定されます。

StringBuilder(Int32, Int32) コンストラクターを呼び出すことによって、StringBuilder オブジェクトの最大容量を明示的に定義できます。 MaxCapacity プロパティに新しい値を割り当てることで最大容量を変更することはできません。これは読み取り専用であるためです。

前のセクションに示すように、既存の容量が不十分な場合は常に、追加のメモリが割り当てられ、 StringBuilder オブジェクトの容量は、 MaxCapacity プロパティによって定義された値まで倍になります。

一般に、既定の容量と最大容量は、ほとんどのアプリに適しています。 これらの値は、次の条件で設定することを検討してください。

  • StringBuilder オブジェクトの最終的なサイズが非常に大きくなる可能性が高い場合は、通常は数メガバイトを超えます。 この場合、初期 Capacity プロパティを大幅に高い値に設定すると、メモリの再割り当てが多くなりすぎないようにすると、パフォーマンス上のメリットが得られる場合があります。
  • メモリが制限されたシステムでコードが実行されている場合。 この場合、メモリ制約のある環境で実行される可能性がある大きな文字列をコードが処理している場合は、 MaxCapacity プロパティを Int32.MaxValue 未満に設定することを検討してください。

StringBuilder オブジェクトをインスタンス化する

次の表に示すように、オーバーロードされた 6 つのクラス コンストラクターのいずれかを呼び出して、 StringBuilder オブジェクトをインスタンス化します。 3 つのコンストラクターは、値が空の文字列である StringBuilder オブジェクトをインスタンス化しますが、その CapacityMaxCapacity の値を異なる方法で設定します。 残りの 3 つのコンストラクターは、特定の文字列値と容量を持つ StringBuilder オブジェクトを定義します。 3 つのコンストラクターのうち 2 つ Int32.MaxValueの既定の最大容量が使用されますが、3 つ目のコンストラクターでは最大容量を設定できます。

Constructor 文字列値 容量 最大容量
StringBuilder() String.Empty 16 Int32.MaxValue
StringBuilder(Int32) String.Empty capacity パラメーターによって定義されます Int32.MaxValue
StringBuilder(Int32, Int32) String.Empty capacity パラメーターによって定義されます maxCapacity パラメーターによって定義されます
StringBuilder(String) value パラメーターによって定義されます 16 または valueLengthのいずれか大きい方 Int32.MaxValue
StringBuilder(String, Int32) value パラメーターによって定義されます capacity パラメーターまたはvalueによって定義されます。 Lengthの方が大きい方です。 Int32.MaxValue
StringBuilder(String, Int32, Int32, Int32) value によって定義されます。 Substring(startIndexlength) capacity パラメーターまたはvalueによって定義されます。 Lengthの方が大きい方です。 Int32.MaxValue

次の例では、これらのコンストラクター オーバーロードのうち 3 つを使用して、 StringBuilder オブジェクトをインスタンス化します。

using System;
using System.Text;

public class Example8
{
    public static void Main()
    {
        string value = "An ordinary string";
        int index = value.IndexOf("An ") + 3;
        int capacity = 0xFFFF;

        // Instantiate a StringBuilder from a string.
        StringBuilder sb1 = new StringBuilder(value);
        ShowSBInfo(sb1);

        // Instantiate a StringBuilder from string and define a capacity.  
        StringBuilder sb2 = new StringBuilder(value, capacity);
        ShowSBInfo(sb2);

        // Instantiate a StringBuilder from substring and define a capacity.  
        StringBuilder sb3 = new StringBuilder(value, index,
                                              value.Length - index,
                                              capacity);
        ShowSBInfo(sb3);
    }

    public static void ShowSBInfo(StringBuilder sb)
    {
        Console.WriteLine("\nValue: {0}", sb.ToString());
        foreach (var prop in sb.GetType().GetProperties())
        {
            if (prop.GetIndexParameters().Length == 0)
                Console.Write("{0}: {1:N0}    ", prop.Name, prop.GetValue(sb));
        }
        Console.WriteLine();
    }
}
// The example displays the following output:
//    Value: An ordinary string
//    Capacity: 18    MaxCapacity: 2,147,483,647    Length: 18
//    
//    Value: An ordinary string
//    Capacity: 65,535    MaxCapacity: 2,147,483,647    Length: 18
//    
//    Value: ordinary string
//    Capacity: 65,535    MaxCapacity: 2,147,483,647    Length: 15
open System.Text

let showSBInfo (sb: StringBuilder) =
    for prop in sb.GetType().GetProperties() do
        if prop.GetIndexParameters().Length = 0 then
            printf $"{prop.Name}: {prop.GetValue sb:N0}    "

    printfn ""

let value = "An ordinary string"
let index = value.IndexOf "An " + 3
let capacity = 0xFFFF

// Instantiate a StringBuilder from a string.
let sb1 = StringBuilder value
showSBInfo sb1

// Instantiate a StringBuilder from string and define a capacity.
let sb2 = StringBuilder(value, capacity)
showSBInfo sb2

// Instantiate a StringBuilder from substring and define a capacity.
let sb3 = StringBuilder(value, index, value.Length - index, capacity)
showSBInfo sb3

// The example displays the following output:
//    Value: An ordinary string
//    Capacity: 18    MaxCapacity: 2,147,483,647    Length: 18
//
//    Value: An ordinary string
//    Capacity: 65,535    MaxCapacity: 2,147,483,647    Length: 18
//
//    Value: ordinary string
//    Capacity: 65,535    MaxCapacity: 2,147,483,647    Length: 15
Imports System.Text

Module Example8
    Public Sub Main()
        Dim value As String = "An ordinary string"
        Dim index As Integer = value.IndexOf("An ") + 3
        Dim capacity As Integer = &HFFFF

        ' Instantiate a StringBuilder from a string.
        Dim sb1 As New StringBuilder(value)
        ShowSBInfo(sb1)

        ' Instantiate a StringBuilder from string and define a capacity.  
        Dim sb2 As New StringBuilder(value, capacity)
        ShowSBInfo(sb2)

        ' Instantiate a StringBuilder from substring and define a capacity.  
        Dim sb3 As New StringBuilder(value, index,
                                   value.Length - index,
                                   capacity)
        ShowSBInfo(sb3)
    End Sub

    Public Sub ShowSBInfo(sb As StringBuilder)
        Console.WriteLine()
        Console.WriteLine("Value: {0}", sb.ToString())
        For Each prop In sb.GetType().GetProperties
            If prop.GetIndexParameters().Length = 0 Then
                Console.Write("{0}: {1:N0}    ", prop.Name, prop.GetValue(sb))
            End If
        Next
        Console.WriteLine()
    End Sub
End Module
' The example displays the following output:
'    Value: An ordinary string
'    Capacity: 18    MaxCapacity: 2,147,483,647    Length: 18
'    
'    Value: An ordinary string
'    Capacity: 65,535    MaxCapacity: 2,147,483,647    Length: 18
'    
'    Value: ordinary string
'    Capacity: 65,535    MaxCapacity: 2,147,483,647    Length: 15

StringBuilder メソッドを呼び出す

StringBuilder インスタンス内の文字列を変更するほとんどのメソッドは、その同じインスタンスへの参照を返します。 これにより、次の 2 つの方法で StringBuilder メソッドを呼び出すことができるようになります。

  • 次の例のように、個々のメソッド呼び出しを行い、戻り値を無視できます。

    using System;
    using System.Text;
    
    public class Example
    {
       public static void Main()
       {
          StringBuilder sb = new StringBuilder();
          sb.Append("This is the beginning of a sentence, ");
          sb.Replace("the beginning of ", "");
          sb.Insert(sb.ToString().IndexOf("a ") + 2, "complete ");
          sb.Replace(",", ".");
          Console.WriteLine(sb.ToString());
       }
    }
    // The example displays the following output:
    //        This is a complete sentence.
    
    open System.Text
    
    let sb = StringBuilder()
    sb.Append "This is the beginning of a sentence, " |> ignore
    sb.Replace("the beginning of ", "") |> ignore
    sb.Insert((string sb).IndexOf "a " + 2, "complete ") |> ignore
    sb.Replace(",", ".") |> ignore
    printfn $"{sb}"
    // The example displays the following output:
    //        This is a complete sentence.
    
    Imports System.Text
    
    Module Example2
        Public Sub Main()
            Dim sb As New StringBuilder()
            sb.Append("This is the beginning of a sentence, ")
            sb.Replace("the beginning of ", "")
            sb.Insert(sb.ToString().IndexOf("a ") + 2, "complete ")
            sb.Replace(",", ".")
            Console.WriteLine(sb.ToString())
        End Sub
    End Module
    ' The example displays the following output:
    '       This is a complete sentence.
    
  • 1 つのステートメントで一連のメソッド呼び出しを行うことができます。 これは、連続する操作をチェーンする単一のステートメントを記述する場合に便利です。 次の例では、前の例の 3 つのメソッド呼び出しを 1 行のコードに統合します。

    using System;
    using System.Text;
    
    public class Example2
    {
        public static void Main()
        {
            StringBuilder sb = new StringBuilder("This is the beginning of a sentence, ");
            sb.Replace("the beginning of ", "").Insert(sb.ToString().IndexOf("a ") + 2,
                                                       "complete ").Replace(",", ".");
            Console.WriteLine(sb.ToString());
        }
    }
    // The example displays the following output:
    //        This is a complete sentence.
    
    open System.Text
    
    let sb = StringBuilder "This is the beginning of a sentence, "
    
    sb
        .Replace("the beginning of ", "")
        .Insert((string sb).IndexOf "a " + 2, "complete ")
        .Replace(",", ".")
    |> ignore
    
    printfn $"{sb}"
    // The example displays the following output:
    //        This is a complete sentence.
    
    Imports System.Text
    
    Module Example3
        Public Sub Main()
            Dim sb As New StringBuilder("This is the beginning of a sentence, ")
            sb.Replace("the beginning of ", "").Insert(sb.ToString().IndexOf("a ") + 2,
                                                     "complete ").Replace(", ", ".")
            Console.WriteLine(sb.ToString())
        End Sub
    End Module
    ' The example displays the following output:
    '       This is a complete sentence.
    

StringBuilder 操作を実行する

StringBuilder クラスのメソッドを使用して、StringBuilder オブジェクト内の文字を反復処理、追加、削除、または変更できます。

StringBuilder 文字を反復処理する

StringBuilder.Chars[] プロパティを使用して、StringBuilder オブジェクト内の文字にアクセスできます。 C# では、 Chars[] はインデクサーです。Visual Basic では、 StringBuilder クラスの既定のプロパティです。 これにより、 Chars[] プロパティを明示的に参照することなく、インデックスのみを使用して個々の文字を設定または取得できます。 StringBuilder オブジェクト内の文字は、インデックス 0 (ゼロ) から始まり、Length - 1 のインデックスを続けます。

次の例は、 Chars[] プロパティを示しています。 StringBuilder オブジェクトに 10 個の乱数を追加し、各文字を反復処理します。 文字の Unicode カテゴリが UnicodeCategory.DecimalDigitNumberされている場合は、数値を 1 だけ減らします (値が 0 の場合は 9 に変更されます)。 この例では、個々の文字の値が変更される前と後の両方で、 StringBuilder オブジェクトの内容を表示します。

using System;
using System.Globalization;
using System.Text;

public class Example3
{
    public static void Main()
    {
        Random rnd = new Random();
        StringBuilder sb = new StringBuilder();

        // Generate 10 random numbers and store them in a StringBuilder.
        for (int ctr = 0; ctr <= 9; ctr++)
            sb.Append(rnd.Next().ToString("N5"));

        Console.WriteLine("The original string:");
        Console.WriteLine(sb.ToString());

        // Decrease each number by one.
        for (int ctr = 0; ctr < sb.Length; ctr++)
        {
            if (Char.GetUnicodeCategory(sb[ctr]) == UnicodeCategory.DecimalDigitNumber)
            {
                int number = (int)Char.GetNumericValue(sb[ctr]);
                number--;
                if (number < 0) number = 9;

                sb[ctr] = number.ToString()[0];
            }
        }
        Console.WriteLine("\nThe new string:");
        Console.WriteLine(sb.ToString());
    }
}
// The example displays the following output:
//    The original string:
//    1,457,531,530.00000940,522,609.000001,668,113,564.000001,998,992,883.000001,792,660,834.00
//    000101,203,251.000002,051,183,075.000002,066,000,067.000001,643,701,043.000001,702,382,508
//    .00000
//    
//    The new string:
//    0,346,420,429.99999839,411,598.999990,557,002,453.999990,887,881,772.999990,681,559,723.99
//    999090,192,140.999991,940,072,964.999991,955,999,956.999990,532,690,932.999990,691,271,497
//    .99999
open System
open System.Globalization
open System.Text

let rnd = Random()
let sb = new StringBuilder()

// Generate 10 random numbers and store them in a StringBuilder.
for _ = 0 to 9 do
    rnd.Next().ToString "N5" |> sb.Append |> ignore

printfn "The original string:"
printfn $"{sb}"

// Decrease each number by one.
for i = 0 to sb.Length - 1 do
    if Char.GetUnicodeCategory(sb[i]) = UnicodeCategory.DecimalDigitNumber then
        let number = Char.GetNumericValue sb.[i] |> int
        let number = number - 1
        let number = if number < 0 then 9 else number
        sb.[i] <- number.ToString()[0]

printfn "\nThe new string:"
printfn $"{sb}"

// The example displays the following output:
//    The original string:
//    1,457,531,530.00000940,522,609.000001,668,113,564.000001,998,992,883.000001,792,660,834.00
//    000101,203,251.000002,051,183,075.000002,066,000,067.000001,643,701,043.000001,702,382,508
//    .00000
//
//    The new string:
//    0,346,420,429.99999839,411,598.999990,557,002,453.999990,887,881,772.999990,681,559,723.99
//    999090,192,140.999991,940,072,964.999991,955,999,956.999990,532,690,932.999990,691,271,497
//    .99999
Imports System.Globalization
Imports System.Text

Module Example4
    Public Sub Main()
        Dim rnd As New Random()
        Dim sb As New StringBuilder()

        ' Generate 10 random numbers and store them in a StringBuilder.
        For ctr As Integer = 0 To 9
            sb.Append(rnd.Next().ToString("N5"))
        Next
        Console.WriteLine("The original string:")
        Console.WriteLine(sb.ToString())
        Console.WriteLine()

        ' Decrease each number by one.
        For ctr As Integer = 0 To sb.Length - 1
            If Char.GetUnicodeCategory(sb(ctr)) = UnicodeCategory.DecimalDigitNumber Then
                Dim number As Integer = CType(Char.GetNumericValue(sb(ctr)), Integer)
                number -= 1
                If number < 0 Then number = 9

                sb(ctr) = number.ToString()(0)
            End If
        Next
        Console.WriteLine("The new string:")
        Console.WriteLine(sb.ToString())
    End Sub
End Module
' The example displays the following output:
'    The original string:
'    1,457,531,530.00000940,522,609.000001,668,113,564.000001,998,992,883.000001,792,660,834.00
'    000101,203,251.000002,051,183,075.000002,066,000,067.000001,643,701,043.000001,702,382,508
'    .00000
'    
'    The new string:
'    0,346,420,429.99999839,411,598.999990,557,002,453.999990,887,881,772.999990,681,559,723.99
'    999090,192,140.999991,940,072,964.999991,955,999,956.999990,532,690,932.999990,691,271,497
'    .99999

Chars[] プロパティで文字ベースのインデックス付けを使用すると、次の条件下では非常に遅くなることがあります。

  • StringBuilder インスタンスが大きい (たとえば、数万文字が含まれている)。
  • StringBuilderは "チャンク" です。つまり、StringBuilder.Appendなどのメソッドの呼び出しが繰り返されると、オブジェクトの StringBuilder.Capacity プロパティが自動的に拡張され、メモリの新しいチャンクが割り当てられます。

文字にアクセスするたびに、チャンクのリンク リスト全体が走査されて、インデックスを付ける適切なバッファーが検索されるため、パフォーマンスが著しく低下します。

Note

大きな "チャンク" StringBuilder オブジェクトの場合でも、1 つまたは少数の文字へのインデックス ベースのアクセスに Chars[] プロパティを使用すると、パフォーマンスに影響を与える可能性はごくわずかです。通常は、 O(n) 操作です。 StringBuilder オブジェクト内の文字を反復処理するときは、パフォーマンスに大きな影響が発生します。これは、O(n^2) 操作でます。

StringBuilder オブジェクトで文字ベースのインデックス付けを使うときにパフォーマンスの問題が発生する場合は、次のいずれかの回避策を使うことができます。

  • ToString メソッドを呼び出して StringBuilder インスタンスを String に変換した後、文字列内の文字にアクセスします。

  • 既存の StringBuilder オブジェクトの内容を、事前にサイズを設定した新しい StringBuilder オブジェクトにコピーします。 新しい StringBuilder オブジェクトはチャンク化していないため、パフォーマンスが向上します。 次に例を示します。

    // sbOriginal is the existing StringBuilder object
    var sbNew = new StringBuilder(sbOriginal.ToString(), sbOriginal.Length);
    
    ' sbOriginal is the existing StringBuilder object
    Dim sbNew = New StringBuilder(sbOriginal.ToString(), sbOriginal.Length)
    
  • StringBuilder(Int32) コンストラクターを呼び出して、StringBuilder オブジェクトの初期容量を、予想される最大サイズにほぼ等しい値に設定します。 このようにすると、StringBuilder が最大容量に達することがほとんどない場合であっても、メモリ ブロック全体が割り当てられることに注意してください。

StringBuilder オブジェクトにテキストを追加する

StringBuilder クラスには、StringBuilder オブジェクトの内容を展開するための次のメソッドが含まれています。

  • Append メソッドは、文字列、部分文字列、文字配列、文字配列の一部、複数回繰り返される 1 つの文字、またはプリミティブ データ型の文字列表現をStringBuilder オブジェクトに追加します。

  • AppendLine メソッドは、StringBuilder オブジェクトに行ターミネータまたは行終端記号と共に文字列を追加します。

  • AppendFormat メソッドは、StringBuilder オブジェクトにcomposite 書式指定文字列を追加します。 結果文字列に含まれるオブジェクトの文字列表現は、現在のシステム カルチャまたは指定したカルチャの書式設定規則を反映できます。

  • Insertメソッドは、文字列、部分文字列、文字列の複数の繰り返し、文字配列、文字配列の一部、またはプリミティブ データ型の文字列表現を、StringBuilder オブジェクト内の指定した位置に挿入します。 位置は 0 から始まるインデックスによって定義されます。

次の例では、 AppendAppendLineAppendFormat、および Insert メソッドを使用して、 StringBuilder オブジェクトのテキストを展開します。

using System;
using System.Text;

public class Example6
{
    public static void Main()
    {
        // Create a StringBuilder object with no text.
        StringBuilder sb = new StringBuilder();
        // Append some text.
        sb.Append('*', 10).Append(" Adding Text to a StringBuilder Object ").Append('*', 10);
        sb.AppendLine("\n");
        sb.AppendLine("Some code points and their corresponding characters:");
        // Append some formatted text.
        for (int ctr = 50; ctr <= 60; ctr++)
        {
            sb.AppendFormat("{0,12:X4} {1,12}", ctr, Convert.ToChar(ctr));
            sb.AppendLine();
        }
        // Find the end of the introduction to the column.
        int pos = sb.ToString().IndexOf("characters:") + 11 +
                  Environment.NewLine.Length;
        // Insert a column header.
        sb.Insert(pos, String.Format("{2}{0,12:X4} {1,12}{2}", "Code Unit",
                                     "Character", "\n"));

        // Convert the StringBuilder to a string and display it.      
        Console.WriteLine(sb.ToString());
    }
}
// The example displays the following output:
//    ********** Adding Text to a StringBuilder Object **********
//    
//    Some code points and their corresponding characters:
//    
//       Code Unit    Character
//            0032            2
//            0033            3
//            0034            4
//            0035            5
//            0036            6
//            0037            7
//            0038            8
//            0039            9
//            003A            :
//            003B            ;
//            003C            <
open System
open System.Text

// Create a StringBuilder object with no text.
let sb = StringBuilder()
// Append some text.
sb
    .Append('*', 10)
    .Append(" Adding Text to a StringBuilder Object ")
    .Append('*', 10)
|> ignore

sb.AppendLine "\n" |> ignore
sb.AppendLine "Some code points and their corresponding characters:" |> ignore
// Append some formatted text.
for i = 50 to 60 do
    sb.AppendFormat("{0,12:X4} {1,12}", i, Convert.ToChar i) |> ignore
    sb.AppendLine() |> ignore

// Find the end of the introduction to the column.
let pos = (string sb).IndexOf("characters:") + 11 + Environment.NewLine.Length
// Insert a column header.
sb.Insert(pos, String.Format("{2}{0,12:X4} {1,12}{2}", "Code Unit", "Character", "\n"))
|> ignore

// Convert the StringBuilder to a string and display it.
printfn $"{sb}"


// The example displays the following output:
//    ********** Adding Text to a StringBuilder Object **********
//
//    Some code points and their corresponding characters:
//
//       Code Unit    Character
//            0032            2
//            0033            3
//            0034            4
//            0035            5
//            0036            6
//            0037            7
//            0038            8
//            0039            9
//            003A            :
//            003B            ;
//            003C            <
Imports System.Text

Module Example7
    Public Sub Main()
        ' Create a StringBuilder object with no text.
        Dim sb As New StringBuilder()
        ' Append some text.
        sb.Append("*"c, 10).Append(" Adding Text to a StringBuilder Object ").Append("*"c, 10)
        sb.AppendLine()
        sb.AppendLine()
        sb.AppendLine("Some code points and their corresponding characters:")
        ' Append some formatted text.
        For ctr = 50 To 60
            sb.AppendFormat("{0,12:X4} {1,12}", ctr, Convert.ToChar(ctr))
            sb.AppendLine()
        Next
        ' Find the end of the introduction to the column.
        Dim pos As Integer = sb.ToString().IndexOf("characters:") + 11 +
                           Environment.NewLine.Length
        ' Insert a column header.
        sb.Insert(pos, String.Format("{2}{0,12:X4} {1,12}{2}", "Code Unit",
                                   "Character", vbCrLf))

        ' Convert the StringBuilder to a string and display it.      
        Console.WriteLine(sb.ToString())
    End Sub
End Module
' The example displays the following output:
'       ********** Adding Text to a StringBuilder Object **********
'       
'       Some code points and their corresponding characters:
'       
'          Code Unit    Character
'               0032            2
'               0033            3
'               0034            4
'               0035            5
'               0036            6
'               0037            7
'               0038            8
'               0039            9
'               003A            :
'               003B            ;
'               003C            <

StringBuilder オブジェクトからテキストを削除する

StringBuilder クラスには、現在のStringBuilder インスタンスのサイズを小さくできるメソッドが含まれています。 Clear メソッドは、すべての文字を削除し、Length プロパティを 0 に設定します。 Remove メソッドは、特定のインデックス位置から始まる指定した文字数を削除します。 さらに、 StringBuilder オブジェクトの末尾から文字を削除するには、その Length プロパティを現在のインスタンスの長さよりも短い値に設定します。

次の例では、 StringBuilder オブジェクトからテキストの一部を削除し、結果の容量、最大容量、および長さのプロパティ値を表示してから、 Clear メソッドを呼び出して、 StringBuilder オブジェクトからすべての文字を削除します。

using System;
using System.Text;

public class Example5
{
    public static void Main()
    {
        StringBuilder sb = new StringBuilder("A StringBuilder object");
        ShowSBInfo(sb);
        // Remove "object" from the text.
        string textToRemove = "object";
        int pos = sb.ToString().IndexOf(textToRemove);
        if (pos >= 0)
        {
            sb.Remove(pos, textToRemove.Length);
            ShowSBInfo(sb);
        }
        // Clear the StringBuilder contents.
        sb.Clear();
        ShowSBInfo(sb);
    }

    public static void ShowSBInfo(StringBuilder sb)
    {
        Console.WriteLine("\nValue: {0}", sb.ToString());
        foreach (var prop in sb.GetType().GetProperties())
        {
            if (prop.GetIndexParameters().Length == 0)
                Console.Write("{0}: {1:N0}    ", prop.Name, prop.GetValue(sb));
        }
        Console.WriteLine();
    }
}
// The example displays the following output:
//    Value: A StringBuilder object
//    Capacity: 22    MaxCapacity: 2,147,483,647    Length: 22
//    
//    Value: A StringBuilder
//    Capacity: 22    MaxCapacity: 2,147,483,647    Length: 16
//    
//    Value:
//    Capacity: 22    MaxCapacity: 2,147,483,647    Length: 0
open System.Text

let showSBInfo (sb: StringBuilder) =
    for prop in sb.GetType().GetProperties() do
        if prop.GetIndexParameters().Length = 0 then
            printf $"{prop.Name}: {prop.GetValue sb:N0}    "

    printfn ""

let sb = StringBuilder "A StringBuilder object"
showSBInfo sb
// Remove "object" from the text.
let textToRemove = "object"
let pos = (string sb).IndexOf textToRemove

if pos >= 0 then
    sb.Remove(pos, textToRemove.Length) |> ignore
    showSBInfo sb

// Clear the StringBuilder contents.
sb.Clear() |> ignore
showSBInfo sb

// The example displays the following output:
//    Value: A StringBuilder object
//    Capacity: 22    MaxCapacity: 2,147,483,647    Length: 22
//
//    Value: A StringBuilder
//    Capacity: 22    MaxCapacity: 2,147,483,647    Length: 16
//
//    Value:
//    Capacity: 22    MaxCapacity: 2,147,483,647    Length: 0
Imports System.Text

Module Example6
    Public Sub Main()
        Dim sb As New StringBuilder("A StringBuilder object")
        ShowSBInfo(sb)
        ' Remove "object" from the text.
        Dim textToRemove As String = "object"
        Dim pos As Integer = sb.ToString().IndexOf(textToRemove)
        If pos >= 0 Then
            sb.Remove(pos, textToRemove.Length)
            ShowSBInfo(sb)
        End If
        ' Clear the StringBuilder contents.
        sb.Clear()
        ShowSBInfo(sb)
    End Sub

    Public Sub ShowSBInfo(sb As StringBuilder)
        Console.WriteLine()
        Console.WriteLine("Value: {0}", sb.ToString())
        For Each prop In sb.GetType().GetProperties
            If prop.GetIndexParameters().Length = 0 Then
                Console.Write("{0}: {1:N0}    ", prop.Name, prop.GetValue(sb))
            End If
        Next
        Console.WriteLine()
    End Sub
End Module
' The example displays the following output:
'    Value: A StringBuilder object
'    Capacity: 22    MaxCapacity: 2,147,483,647    Length: 22
'    
'    Value: A StringBuilder
'    Capacity: 22    MaxCapacity: 2,147,483,647    Length: 16
'    
'    Value:
'    Capacity: 22    MaxCapacity: 2,147,483,647    Length: 0

StringBuilder オブジェクト内のテキストを変更する

StringBuilder.Replace メソッドは、StringBuilder オブジェクト全体または特定の文字範囲の文字または文字列のすべての出現箇所を置き換えます。 次の例では、 Replace メソッドを使用して、 StringBuilder オブジェクト内のすべての感嘆符 (!) を疑問符 (?) に置き換えます。

using System;
using System.Text;

public class Example13
{
    public static void Main()
    {
        StringBuilder MyStringBuilder = new StringBuilder("Hello World!");
        MyStringBuilder.Replace('!', '?');
        Console.WriteLine(MyStringBuilder);
    }
}
// The example displays the following output:
//       Hello World?
open System.Text

let myStringBuilder = StringBuilder "Hello World!"
myStringBuilder.Replace('!', '?') |> ignore
printfn $"{myStringBuilder}"

// The example displays the following output:
//       Hello World?
Imports System.Text

Module Example
   Public Sub Main()
      Dim MyStringBuilder As New StringBuilder("Hello World!")
      MyStringBuilder.Replace("!"c, "?"c)
      Console.WriteLine(MyStringBuilder)
   End Sub
End Module
' The example displays the following output:
'       Hello World?

StringBuilder オブジェクト内のテキストを検索する

StringBuilder クラスには、String クラスによって提供されるString.ContainsString.IndexOf、および String.StartsWith メソッドと同様のメソッドは含まれていません。これにより、オブジェクトで特定の文字または部分文字列を検索できます。 部分文字列の存在または開始文字の位置を決定するには、文字列検索メソッドまたは正規表現メソッドを使用して String 値を検索する必要があります。 次の表に示すように、このような検索を実装する方法は 4 つあります。

手法 長所 デメリット
文字列値を StringBuilder オブジェクトに追加する前に検索します。 部分文字列が存在するかどうかを判断するのに役立ちます。 部分文字列のインデックス位置が重要な場合は使用できません。
ToStringを呼び出し、返されたString オブジェクトを検索します。 すべてのテキストを StringBuilder オブジェクトに割り当ててから変更を開始する場合に簡単に使用できます。 すべてのテキストをStringBuilder オブジェクトに追加する前に変更を加える必要がある場合は、ToStringを繰り返し呼び出すのが面倒です。

変更を加える場合は、 StringBuilder オブジェクトのテキストの末尾から作業することを忘れないでください。
Chars[] プロパティを使用して、一連の文字を順番に検索します。 個々の文字や小さな部分文字列に関心がある場合に便利です。 検索する文字数が多い場合や、検索ロジックが複雑な場合は煩雑です。

メソッド呼び出しを繰り返して非常に大きくなったオブジェクトのパフォーマンスが非常に低くなります。
StringBuilder オブジェクトをString オブジェクトに変換し、String オブジェクトに対して変更を実行します。 変更の数が少ない場合に便利です。 変更の数が多い場合は、 StringBuilder クラスのパフォーマンス上の利点を否定します。

これらの手法について詳しく調べてみましょう。

  • 検索の目的が、特定の部分文字列が存在するかどうかを判断することです (つまり、部分文字列の位置に関心がない場合)、文字列を検索してから、 StringBuilder オブジェクトに格納できます。 次の例では、1 つの可能な実装を示します。 コンストラクターにStringBuilder オブジェクトへの参照と文字列内で検索する部分文字列を渡すStringBuilderFinder クラスを定義します。 この例では、記録された温度が華氏と摂氏のどちらにあるかを判断し、 StringBuilder オブジェクトの先頭に適切な入門テキストを追加します。 乱数ジェネレーターを使用して、摂氏または華氏度のデータを含む配列を選択します。

    using System;
    using System.Text;
    
    public class Example9
    {
        public static void Main()
        {
            Random rnd = new Random();
            string[] tempF = { "47.6F", "51.3F", "49.5F", "62.3F" };
            string[] tempC = { "21.2C", "16.1C", "23.5C", "22.9C" };
            string[][] temps = { tempF, tempC };
    
            StringBuilder sb = new StringBuilder();
            var f = new StringBuilderFinder(sb, "F");
            var baseDate = new DateTime(2013, 5, 1);
            String[] temperatures = temps[rnd.Next(2)];
            bool isFahrenheit = false;
            foreach (var temperature in temperatures)
            {
                if (isFahrenheit)
                    sb.AppendFormat("{0:d}: {1}\n", baseDate, temperature);
                else
                    isFahrenheit = f.SearchAndAppend(String.Format("{0:d}: {1}\n",
                                                     baseDate, temperature));
                baseDate = baseDate.AddDays(1);
            }
            if (isFahrenheit)
            {
                sb.Insert(0, "Average Daily Temperature in Degrees Fahrenheit");
                sb.Insert(47, "\n\n");
            }
            else
            {
                sb.Insert(0, "Average Daily Temperature in Degrees Celsius");
                sb.Insert(44, "\n\n");
            }
            Console.WriteLine(sb.ToString());
        }
    }
    
    public class StringBuilderFinder
    {
        private StringBuilder sb;
        private String text;
    
        public StringBuilderFinder(StringBuilder sb, String textToFind)
        {
            this.sb = sb;
            this.text = textToFind;
        }
    
        public bool SearchAndAppend(String stringToSearch)
        {
            sb.Append(stringToSearch);
            return stringToSearch.Contains(text);
        }
    }
    // The example displays output similar to the following:
    //    Average Daily Temperature in Degrees Celsius
    //    
    //    5/1/2013: 21.2C
    //    5/2/2013: 16.1C
    //    5/3/2013: 23.5C
    //    5/4/2013: 22.9C
    
    open System
    open System.Text
    
    type StringBuilderFinder(sb: StringBuilder, textToFind: string) =
        member _.SearchAndAppend(stringToSearch: string) =
            sb.Append stringToSearch |> ignore
            stringToSearch.Contains textToFind
    
    let tempF = [| "47.6F"; "51.3F"; "49.5F"; "62.3F" |]
    let tempC = [| "21.2C"; "16.1C"; "23.5C"; "22.9C" |]
    let temps = [| tempF; tempC |]
    
    let sb = StringBuilder()
    let f = StringBuilderFinder(sb, "F")
    let temperatures = temps[Random.Shared.Next(2)]
    let mutable baseDate = DateTime(2013, 5, 1)
    let mutable isFahrenheit = false
    
    for temperature in temperatures do
        if isFahrenheit then
            sb.AppendFormat("{0:d}: {1}\n", baseDate, temperature) |> ignore
        else
            isFahrenheit <- $"{baseDate:d}: {temperature}\n" |> f.SearchAndAppend
    
        baseDate <- baseDate.AddDays 1
    
    if isFahrenheit then
        sb.Insert(0, "Average Daily Temperature in Degrees Fahrenheit") |> ignore
        sb.Insert(47, "\n\n") |> ignore
    
    else
        sb.Insert(0, "Average Daily Temperature in Degrees Celsius") |> ignore
        sb.Insert(44, "\n\n") |> ignore
    
    printfn $"{sb}"
    
    // The example displays output similar to the following:
    //    Average Daily Temperature in Degrees Celsius
    //
    //    5/1/2013: 21.2C
    //    5/2/2013: 16.1C
    //    5/3/2013: 23.5C
    //    5/4/2013: 22.9C
    
    Imports System.Text
    
    Module Example9
        Public Sub Main()
            Dim rnd As New Random()
            Dim tempF() As String = {"47.6F", "51.3F", "49.5F", "62.3F"}
            Dim tempC() As String = {"21.2C", "16.1C", "23.5C", "22.9C"}
            Dim temps()() As String = {tempF, tempC}
    
            Dim sb As StringBuilder = New StringBuilder()
            Dim f As New StringBuilderFinder(sb, "F")
            Dim baseDate As New DateTime(2013, 5, 1)
            Dim temperatures() As String = temps(rnd.Next(2))
            Dim isFahrenheit As Boolean = False
            For Each temperature In temperatures
                If isFahrenheit Then
                    sb.AppendFormat("{0:d}: {1}{2}", baseDate, temperature, vbCrLf)
                Else
                    isFahrenheit = f.SearchAndAppend(String.Format("{0:d}: {1}{2}",
                                                 baseDate, temperature, vbCrLf))
                End If
                baseDate = baseDate.AddDays(1)
            Next
            If isFahrenheit Then
                sb.Insert(0, "Average Daily Temperature in Degrees Fahrenheit")
                sb.Insert(47, vbCrLf + vbCrLf)
            Else
                sb.Insert(0, "Average Daily Temperature in Degrees Celsius")
                sb.Insert(44, vbCrLf + vbCrLf)
            End If
            Console.WriteLine(sb.ToString())
        End Sub
    End Module
    
    Public Class StringBuilderFinder
       Private sb As StringBuilder
       Private text As String
       
       Public Sub New(sb As StringBuilder, textToFind As String)
          Me.sb = sb
          text = textToFind
       End Sub
       
       Public Function SearchAndAppend(stringToSearch As String) As Boolean
          sb.Append(stringToSearch)
          Return stringToSearch.Contains(text)
       End Function
    End Class
    ' The example displays output similar to the following:
    '    Average Daily Temperature in Degrees Celsius
    '    
    '    5/1/2013: 21.2C
    '    5/2/2013: 16.1C
    '    5/3/2013: 23.5C
    '    5/4/2013: 22.9C
    
  • StringBuilder.ToString メソッドを呼び出して、StringBuilder オブジェクトをString オブジェクトに変換します。 文字列を検索するには、 String.LastIndexOfString.StartsWithなどのメソッドを使用するか、正規表現と Regex クラスを使用してパターンを検索します。 StringBuilderオブジェクトとStringオブジェクトの両方で UTF-16 エンコードを使用して文字を格納するため、文字、部分文字列、および正規表現の一致のインデックス位置は両方のオブジェクトで同じです。 これにより、 StringBuilder メソッドを使用して、そのテキストが String オブジェクト内にあるのと同じ位置で変更を行うことができます。

    Note

    この方法を採用する場合は、StringBuilder オブジェクトを文字列に繰り返し変換する必要がないように、StringBuilder オブジェクトの末尾から先頭に作業する必要があります。

    このアプローチの例を次に示します。 これは、 StringBuilder オブジェクトに英語のアルファベットの各文字の4回の出現を格納します。 次に、テキストを String オブジェクトに変換し、正規表現を使用して各 4 文字シーケンスの開始位置を識別します。 最後に、最初のシーケンスを除く各 4 文字シーケンスの前にアンダースコアを追加し、シーケンスの最初の文字を大文字に変換します。

    using System;
    using System.Text;
    using System.Text.RegularExpressions;
    
    public class Example10
    {
        public static void Main()
        {
            // Create a StringBuilder object with 4 successive occurrences 
            // of each character in the English alphabet. 
            StringBuilder sb = new StringBuilder();
            for (ushort ctr = (ushort)'a'; ctr <= (ushort)'z'; ctr++)
                sb.Append(Convert.ToChar(ctr), 4);
    
            // Create a parallel string object.
            String sbString = sb.ToString();
            // Determine where each new character sequence begins.
            String pattern = @"(\w)\1+";
            MatchCollection matches = Regex.Matches(sbString, pattern);
    
            // Uppercase the first occurrence of the sequence, and separate it
            // from the previous sequence by an underscore character.
            for (int ctr = matches.Count - 1; ctr >= 0; ctr--)
            {
                Match m = matches[ctr];
                sb[m.Index] = Char.ToUpper(sb[m.Index]);
                if (m.Index > 0) sb.Insert(m.Index, "_");
            }
            // Display the resulting string.
            sbString = sb.ToString();
            int line = 0;
            do
            {
                int nChars = line * 80 + 79 <= sbString.Length ?
                                    80 : sbString.Length - line * 80;
                Console.WriteLine(sbString.Substring(line * 80, nChars));
                line++;
            } while (line * 80 < sbString.Length);
        }
    }
    // The example displays the following output:
    //    Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_Oooo_Pppp_
    //    Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz
    
    open System
    open System.Text
    open System.Text.RegularExpressions
    
    // Create a StringBuilder object with 4 successive occurrences
    // of each character in the English alphabet.
    let sb = StringBuilder()
    
    for char in 'a' .. 'z' do
        sb.Append(char, 4) |> ignore
    
    // Create a parallel string object.
    let sbString = string sb
    // Determine where each new character sequence begins.
    let pattern = @"(\w)\1+"
    let matches = Regex.Matches(sbString, pattern)
    
    // Uppercase the first occurrence of the sequence, and separate it
    // from the previous sequence by an underscore character.
    for i = matches.Count - 1 downto 0 do
        let m = matches[i]
        sb[m.Index] <- Char.ToUpper sb[m.Index]
    
        if m.Index > 0 then
            sb.Insert(m.Index, "_") |> ignore
    
    // Display the resulting string.
    let sbString2 = string sb
    
    for line = 0 to (sbString2.Length - 1) / 80 do
        let nChars =
            if line * 80 + 79 <= sbString2.Length then
                80
            else
                sbString2.Length - line * 80
    
        printfn $"{sbString2.Substring(line * 80, nChars)}"
    
    
    // The example displays the following output:
    //    Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_Oooo_Pppp_
    //    Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz
    
    Imports System.Text
    Imports System.Text.RegularExpressions
    
    Module Example10
        Public Sub Main()
            ' Create a StringBuilder object with 4 successive occurrences 
            ' of each character in the English alphabet. 
            Dim sb As New StringBuilder()
            For ctr As UShort = AscW("a") To AscW("z")
                sb.Append(ChrW(ctr), 4)
            Next
            ' Create a parallel string object.
            Dim sbString As String = sb.ToString()
            ' Determine where each new character sequence begins.
            Dim pattern As String = "(\w)\1+"
            Dim matches As MatchCollection = Regex.Matches(sbString, pattern)
    
            ' Uppercase the first occurrence of the sequence, and separate it
            ' from the previous sequence by an underscore character.
            For ctr As Integer = matches.Count - 1 To 0 Step -1
                Dim m As Match = matches(ctr)
                sb.Chars(m.Index) = Char.ToUpper(sb.Chars(m.Index))
                If m.Index > 0 Then sb.Insert(m.Index, "_")
            Next
            ' Display the resulting string.
            sbString = sb.ToString()
            Dim line As Integer = 0
            Do
                Dim nChars As Integer = If(line * 80 + 79 <= sbString.Length,
                                        80, sbString.Length - line * 80)
                Console.WriteLine(sbString.Substring(line * 80, nChars))
                line += 1
            Loop While line * 80 < sbString.Length
        End Sub
    End Module
    ' The example displays the following output:
    '    Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_Oooo_Pppp_
    '    Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz
    
  • StringBuilder.Chars[] プロパティを使用して、StringBuilder オブジェクト内の文字の範囲を順番に検索します。 検索する文字数が多い場合や、検索ロジックが特に複雑な場合、この方法は実用的でない可能性があります。 非常に大きなチャンクされた StringBuilder オブジェクトに対する文字単位のインデックスベースのアクセスのパフォーマンスへの影響については、 StringBuilder.Chars[] プロパティのドキュメントを参照してください。

    次の例は、前の例と同じ機能ですが、実装では異なります。 Chars[] プロパティを使用して、文字値が変更されたことを検出し、その位置にアンダースコアを挿入し、新しいシーケンスの最初の文字を大文字に変換します。

    using System;
    using System.Text;
    
    public class Example11
    {
        public static void Main()
        {
            // Create a StringBuilder object with 4 successive occurrences 
            // of each character in the English alphabet. 
            StringBuilder sb = new StringBuilder();
            for (ushort ctr = (ushort)'a'; ctr <= (ushort)'z'; ctr++)
                sb.Append(Convert.ToChar(ctr), 4);
    
            // Iterate the text to determine when a new character sequence occurs.
            int position = 0;
            Char current = '\u0000';
            do
            {
                if (sb[position] != current)
                {
                    current = sb[position];
                    sb[position] = Char.ToUpper(sb[position]);
                    if (position > 0)
                        sb.Insert(position, "_");
                    position += 2;
                }
                else
                {
                    position++;
                }
            } while (position <= sb.Length - 1);
            // Display the resulting string.
            String sbString = sb.ToString();
            int line = 0;
            do
            {
                int nChars = line * 80 + 79 <= sbString.Length ?
                                    80 : sbString.Length - line * 80;
                Console.WriteLine(sbString.Substring(line * 80, nChars));
                line++;
            } while (line * 80 < sbString.Length);
        }
    }
    // The example displays the following output:
    //    Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_Oooo_Pppp_
    //    Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz
    
    open System
    open System.Text
    
    // Create a StringBuilder object with 4 successive occurrences
    // of each character in the English alphabet.
    let sb = StringBuilder()
    
    for char in 'a' .. 'z' do
        sb.Append(char, 4) |> ignore
    
    // Iterate the text to determine when a new character sequence occurs.
    let mutable position = 0
    let mutable current = '\u0000'
    
    while position <= sb.Length - 1 do
        if sb[position] <> current then
            current <- sb[position]
            sb[position] <- Char.ToUpper sb[position]
    
            if position > 0 then
                sb.Insert(position, "_") |> ignore
    
            position <- position + 2
    
        else
            position <- position + 1
    
    // Display the resulting string.
    let sbString = string sb
    
    for line = 0 to (sbString.Length - 1) / 80 do
        let nChars =
            if line * 80 + 79 <= sbString.Length then
                80
            else
                sbString.Length - line * 80
    
        printfn $"{sbString.Substring(line * 80, nChars)}"
    
    // The example displays the following output:
    //    Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_Oooo_Pppp_
    //    Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz
    
    Imports System.Text
    
    Module Example11
        Public Sub Main()
            ' Create a StringBuilder object with 4 successive occurrences 
            ' of each character in the English alphabet. 
            Dim sb As New StringBuilder()
            For ctr As UShort = AscW("a") To AscW("z")
                sb.Append(ChrW(ctr), 4)
            Next
            ' Iterate the text to determine when a new character sequence occurs.
            Dim position As Integer = 0
            Dim current As Char = ChrW(0)
            Do
                If sb(position) <> current Then
                    current = sb(position)
                    sb(position) = Char.ToUpper(sb(position))
                    If position > 0 Then sb.Insert(position, "_")
                    position += 2
                Else
                    position += 1
                End If
            Loop While position <= sb.Length - 1
            ' Display the resulting string.
            Dim sbString As String = sb.ToString()
            Dim line As Integer = 0
            Do
                Dim nChars As Integer = If(line * 80 + 79 <= sbString.Length,
                                        80, sbString.Length - line * 80)
                Console.WriteLine(sbString.Substring(line * 80, nChars))
                line += 1
            Loop While line * 80 < sbString.Length
        End Sub
    End Module
    ' The example displays the following output:
    '    Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_Oooo_Pppp_
    '    Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz
    
  • 変更されていないテキストをすべて StringBuilder オブジェクトに格納し、 StringBuilder.ToString メソッドを呼び出して StringBuilder オブジェクトを String オブジェクトに変換し、 String オブジェクトに対して変更を実行します。 この方法は、いくつかの変更しかない場合に使用できます。そうしないと、変更できない文字列を操作するコストが、 StringBuilder オブジェクトを使用するパフォーマンス上の利点を損なう可能性があります。

    次の例は、前の 2 つの例と同じ機能ですが、実装では異なります。 StringBuilder オブジェクトを作成し、String オブジェクトに変換した後、正規表現を使用して文字列に対して残りの変更をすべて実行します。 Regex.Replace(String, String, MatchEvaluator) メソッドはラムダ式を使用して、各一致で置換を実行します。

    using System;
    using System.Text;
    using System.Text.RegularExpressions;
    
    public class Example12
    {
        public static void Main()
        {
            // Create a StringBuilder object with 4 successive occurrences 
            // of each character in the English alphabet. 
            StringBuilder sb = new StringBuilder();
            for (ushort ctr = (ushort)'a'; ctr <= (ushort)'z'; ctr++)
                sb.Append(Convert.ToChar(ctr), 4);
    
            // Convert it to a string.
            String sbString = sb.ToString();
    
            // Use a regex to uppercase the first occurrence of the sequence, 
            // and separate it from the previous sequence by an underscore.
            string pattern = @"(\w)(\1+)";
            sbString = Regex.Replace(sbString, pattern,
                                     m => (m.Index > 0 ? "_" : "") +
                                     m.Groups[1].Value.ToUpper() +
                                     m.Groups[2].Value);
    
            // Display the resulting string.
            int line = 0;
            do
            {
                int nChars = line * 80 + 79 <= sbString.Length ?
                                    80 : sbString.Length - line * 80;
                Console.WriteLine(sbString.Substring(line * 80, nChars));
                line++;
            } while (line * 80 < sbString.Length);
        }
    }
    // The example displays the following output:
    //    Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_Oooo_Pppp_
    //    Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz
    
    open System.Text
    open System.Text.RegularExpressions
    
    // Create a StringBuilder object with 4 successive occurrences
    // of each character in the English alphabet.
    let sb = StringBuilder()
    
    for char in 'a' .. 'z' do
        sb.Append(char, 4) |> ignore
    
    // Convert it to a string.
    let sbString = string sb
    
    // Use a regex to uppercase the first occurrence of the sequence,
    // and separate it from the previous sequence by an underscore.
    let pattern = @"(\w)(\1+)"
    
    let sbStringReplaced =
        Regex.Replace(
            sbString,
            pattern,
            fun m ->
                (if m.Index > 0 then "_" else "")
                + m.Groups[ 1 ].Value.ToUpper()
                + m.Groups[2].Value
        )
    
    // Display the resulting string.
    for line = 0 to (sbStringReplaced.Length - 1) / 80 do
        let nChars =
            if line * 80 + 79 <= sbStringReplaced.Length then
                80
            else
                sbStringReplaced.Length - line * 80
    
        printfn $"{sbStringReplaced.Substring(line * 80, nChars)}"
    
    // The example displays the following output:
    //    Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_Oooo_Pppp_
    //    Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz
    
    Imports System.Text
    Imports System.Text.RegularExpressions
    
    Module Example12
        Public Sub Main()
            ' Create a StringBuilder object with 4 successive occurrences 
            ' of each character in the English alphabet. 
            Dim sb As New StringBuilder()
            For ctr As UShort = AscW("a") To AscW("z")
                sb.Append(ChrW(ctr), 4)
            Next
            ' Convert it to a string.
            Dim sbString As String = sb.ToString()
    
            ' Use a regex to uppercase the first occurrence of the sequence, 
            ' and separate it from the previous sequence by an underscore.
            Dim pattern As String = "(\w)(\1+)"
            sbString = Regex.Replace(sbString, pattern,
                                   Function(m) If(m.Index > 0, "_", "") +
                                               m.Groups(1).Value.ToUpper +
                                               m.Groups(2).Value)
    
            ' Display the resulting string.
            Dim line As Integer = 0
            Do
                Dim nChars As Integer = If(line * 80 + 79 <= sbString.Length,
                                        80, sbString.Length - line * 80)
                Console.WriteLine(sbString.Substring(line * 80, nChars))
                line += 1
            Loop While line * 80 < sbString.Length
        End Sub
    End Module
    ' The example displays the following output:
    '    Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_Oooo_Pppp_
    '    Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz
    

StringBuilder オブジェクトを文字列に変換する

StringBuilder オブジェクトで表される文字列を String パラメーターを持つメソッドに渡すかそれをユーザー インターフェイスに表示するには、事前に StringBuilder オブジェクトを String オブジェクトに変換する必要があります。 この変換を実行するには、 StringBuilder.ToString メソッドを呼び出します。 図については、 ToString メソッドを呼び出して、正規表現メソッドに渡すことができるように、 StringBuilder オブジェクトを文字列に変換する前の例を参照してください。