DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

文字列を連結する

文字列を連結するには、連結演算子の + (VB.NETでは & でも可)を使います。

VB.NET
コードを隠すコードを選択
Dim s1 As String = "こんにちは。"
Dim s2 As String = "さようなら。"

'2つの文字列を連結する
Dim s3 As String = s1 & s2

's3は"こんにちは。さようなら。"となる
C#
コードを隠すコードを選択
string s1 = "こんにちは。";
string s2 = "さようなら。";

//2つの文字列を連結する
string s3 = s1 + s2;

//s3は"こんにちは。さようなら。"となる
補足:「"こんにちは。" + "さようなら。"」のように文字列リテラルを直接つなげると、コンパイラが自動的に「"こんにちは。さようなら。"」とするようです。

String.Concatと + の違い

文字列を連結するメソッドとして、String.Concatメソッドがあります。String.Concatメソッドを使ったときと連結演算子を使ったときの違いは、全くありません。ビルドするとどちらもString.Concatが使われるようになります。

Concatメソッドにはパラメータに文字列を2つから4つ指定できるオーバーロードがあります。さらに、文字列の配列をパラメータに指定できるオーバーロードもあります。連結演算子で2〜4の文字列を連結するときは前者を、5個以上の文字列を連結するときは後者を使用するようです。

VB.NET
コードを隠すコードを選択
Dim s1 As String = "こんにちは。"
Dim s2 As String = "さようなら。"

'2つの文字列を連結する
Dim s3 As String = s1 & s2
'Concatを使っても全く同じ
Dim s4 As String = String.Concat(s1, s2)
C#
コードを隠すコードを選択
string s1 = "こんにちは。";
string s2 = "さようなら。";

//2つの文字列を連結する
string s3 = s1 + s2;
//Concatを使っても全く同じ
string s4 = string.Concat(s1, s2);

+ と & の違い

VB.NETには、 + と & の2つの連結演算子があります。2つの文字列型(String型)を連結する場合は、どちらを使用しても同じです。ただし、片方(もしくは両方)が文字列型でない場合は、+ 演算子では文字列の連結とはならず、数値の足し算と判断されてしまうかもしれません(詳しくは、「+ 演算子 (Visual Basic)」)。よってMSDNでは、文字列の連結では + ではなく、 & を使うべきとされています。

StringBuilder.Appendメソッド

StringBuilderクラスのAppendメソッドを使って、文字列を連結することもできます。1,2回文字列を連結する程度であれば連結演算子を使用した方がパフォーマンスが良いですが、例えばループを使って何回も文字列を追加していくような場合は、StringBuilderクラスを使用した方がパフォーマンスが向上します。詳しくは、「StringBuilderのパフォーマンス」をご覧ください。

VB.NET
コードを隠すコードを選択
Dim s1 As String = "こんにちは。"
Dim s2 As String = "さようなら。"

'StringBuilder.Appendを使って、2つの文字列を連結する
Dim sb As New System.Text.StringBuilder()
sb.Append(s1)
sb.Append(s2)
Dim s3 As String = sb.ToString()

's3は"こんにちは。さようなら。"となる
C#
コードを隠すコードを選択
string s1 = "こんにちは。";
string s2 = "さようなら。";

//StringBuilder.Appendを使って、2つの文字列を連結する
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(s1);
sb.Append(s2);
string s3 = sb.ToString();

//s3は"こんにちは。さようなら。"となる

String.Formatメソッド

「連結」というより「1つにまとめる」という感じですが、String.Formatメソッドを使うこともできます。ただし、後述するパフォーマンスについての説明にあるように、String.Formatメソッドは他の方法と比べてパフォーマンスが劣ります。

VB.NET
コードを隠すコードを選択
Dim s1 As String = "こんにちは。"
Dim s2 As String = "さようなら。"

'String.Formatを使って、2つの文字列を連結する
Dim s3 As String = String.Format("{0}{1}", s1, s2)

's3は"こんにちは。さようなら。"となる
C#
コードを隠すコードを選択
string s1 = "こんにちは。";
string s2 = "さようなら。";

//String.Formatを使って、2つの文字列を連結する
string s3 = string.Format("{0}{1}", s1, s2);

//s3は"こんにちは。さようなら。"となる

文字列の配列を連結する

文字列の配列の要素を連結するには、String.Joinメソッドを使用します。String.Joinメソッドは、連結する要素と要素の間に入れる文字列(区切り記号)を指定することができます。また、連結する要素の範囲を指定することもできます。

文字列の配列を連結するには、String.Concatメソッドや、ループして連結演算子やStringBuilder.Appendなどを使う方法もありますが、「StringBuilder vs. String / Fast String Operations with .NET 2.0」によると、String.Joinメソッドを使う方法が一番速いそうです。

以下にString.Joinを使用した例を示します。

VB.NET
コードを隠すコードを選択
Dim strAry As String() = New String() {"今日", "は", "いい", "天気", "です", "。"}

'区切り記号なしで、文字列の配列を連結する
Dim s1 As String = String.Join("", strAry)
'今日はいい天気です。

'区切り記号を"|"にして連結する
Dim s2 As String = String.Join("|", strAry)
'今日|は|いい|天気|です|。

'3番目から最後までの要素を連結する
Dim s3 As String = String.Join("", strAry, 2, strAry.Length - 2)
'いい天気です。
C#
コードを隠すコードを選択
string[] strAry = new string[] { "今日", "は", "いい", "天気", "です", "。" };

//区切り記号なしで、文字列の配列を連結する
string s1 = string.Join("", strAry);
//今日はいい天気です。

//区切り記号を"|"にして連結する
string s2 = string.Join("|", strAry);
//今日|は|いい|天気|です|。

//3番目から最後までの要素を連結する
string s3 = string.Join("", strAry, 2, strAry.Length - 2);
//いい天気です。

複数の文字列を連結する時のパフォーマンス

ここからは、複数の文字列を連結する時、どのようにすればパフォーマンスが向上するかについて説明します。

連結演算子、String.Format、StringBuilder.Appendの比較

C# String Concat Programs」によると、複数の文字列を連結する時、連結演算子やConcatメソッドを使って一度に連結する方法が、String.FormatやStringBuilder.Appendメソッドを使った方法よりもパフォーマンスが良いということです。

私も以下のようなコードで試してみましたが(.NET Framework 4.0)、確かに連結演算子で一度に連結する方法が一番速かったです。

VB.NET
コードを隠すコードを選択
Dim s1 As New String("あ"c, 10)
Dim s2 As New String("い"c, 10)
Dim s3 As New String("う"c, 10)
Dim s4 As New String("え"c, 10)

'連結演算子で連結する
Dim sw1 As New System.Diagnostics.Stopwatch()
sw1.Start()
For i As Integer = 0 To 10000000
    Dim r1 As String = s1 & s2 & s3 & s4
Next
sw1.Stop()
Console.WriteLine(sw1.ElapsedTicks)
'1863170

'String.Formatで連結する
Dim sw2 As New System.Diagnostics.Stopwatch()
sw2.Start()
For i As Integer = 0 To 10000000
    Dim r2 As String = String.Format("{0}{1}{2}{3}", s1, s2, s3, s4)
Next
sw2.Stop()
Console.WriteLine(sw2.ElapsedTicks)
'8028348

'StringBuilder.Appendで連結する
Dim sw3 As New System.Diagnostics.Stopwatch()
sw3.Start()
For i As Integer = 0 To 10000000
    Dim sb As New System.Text.StringBuilder()
    sb.Append(s1).Append(s2).Append(s3).Append(s4)
    Dim r3 As String = sb.ToString()
Next
sw3.Stop()
Console.WriteLine(sw3.ElapsedTicks)
'5184637
C#
コードを隠すコードを選択
string s1 = new string('あ', 10);
string s2 = new string('い', 10);
string s3 = new string('う', 10);
string s4 = new string('え', 10);

//連結演算子で連結する
System.Diagnostics.Stopwatch sw1 = new System.Diagnostics.Stopwatch();
sw1.Start();
for (int i = 0; i < 10000000; i++)
{
    string r1 = s1 + s2 + s3 + s4;
}
sw1.Stop();
Console.WriteLine(sw1.ElapsedTicks);
//1863170

//String.Formatで連結する
System.Diagnostics.Stopwatch sw2 = new System.Diagnostics.Stopwatch();
sw2.Start();
for (int i = 0; i < 10000000; i++)
{
    string r2 = string.Format("{0}{1}{2}{3}", s1, s2, s3, s4);
}
sw2.Stop();
Console.WriteLine(sw2.ElapsedTicks);
//8028348

//StringBuilder.Appendで連結する
System.Diagnostics.Stopwatch sw3 = new System.Diagnostics.Stopwatch();
sw3.Start();
for (int i = 0; i < 10000000; i++)
{
    System.Text.StringBuilder sb = new System.Text.StringBuilder();
    sb.Append(s1).Append(s2).Append(s3).Append(s4);
    string r3 = sb.ToString();
}
sw3.Stop();
Console.WriteLine(sw3.ElapsedTicks);
//5184637

ただし、一度に連結するのではなく、例えばループを使って次々と文字列を追加していくようなケースでは、StringBuilderのパフォーマンスが優れています。詳しくは、「StringBuilderのパフォーマンス」で説明しています。

5つの文字列を連結するときは、2回に分けたほうが速い

ここからは、あまり役に立たない余談です。

同じく「C# String Concat Programs」によると、さらに面白いことに、5つの文字列を連結する場合は、一度に連結するよりも、4つの文字列を連結させてから残りの文字列を連結させた方が高速だということです。つまり、Concatメソッドの文字列の配列をパラメータとするオーバーロードは、それ以外のオーバーロードと比べてパフォーマンスが大きく劣るということです。

私も実際に以下のようなコードで試してみましたが、確かに2回に分けた方が速かったです。また、5つの文字列を4と1に分けるよりも、3と2に分けた方が若干速かったです。

VB.NET
コードを隠すコードを選択
Dim s1 As New String("あ"c, 10)
Dim s2 As New String("い"c, 10)
Dim s3 As New String("う"c, 10)
Dim s4 As New String("え"c, 10)
Dim s5 As New String("お"c, 10)

'一度に連結する
Dim sw1 As New System.Diagnostics.Stopwatch()
sw1.Start()
For i As Integer = 0 To 50000000
    Dim r1 As String = s1 & s2 & s3 & s4 & s5
Next
sw1.Stop()
Console.WriteLine(sw1.ElapsedTicks)
'21527242

'2回に分けて連結する(4,1に分ける)
Dim sw2 As New System.Diagnostics.Stopwatch()
sw2.Start()
For i As Integer = 0 To 50000000
    Dim r2 As String = s1 & s2 & s3 & s4
    r2 += s5
Next
sw2.Stop()
Console.WriteLine(sw2.ElapsedTicks)
'14886505

'2回に分けて連結する(3,2に分ける)
Dim sw3 As New System.Diagnostics.Stopwatch()
sw3.Start()
For i As Integer = 0 To 50000000
    Dim r3 As String = s1 & s2 & s3
    r3 += s4 & s5
Next
sw3.Stop()
Console.WriteLine(sw3.ElapsedTicks)
'13423224
C#
コードを隠すコードを選択
string s1 = new string('あ', 10);
string s2 = new string('い', 10);
string s3 = new string('う', 10);
string s4 = new string('え', 10);
string s5 = new string('お', 10);

//一度に連結する
System.Diagnostics.Stopwatch sw1 = new System.Diagnostics.Stopwatch();
sw1.Start();
for (int i = 0; i < 50000000; i++)
{
    string r1 = s1 + s2 + s3 + s4 + s5;
}
sw1.Stop();
Console.WriteLine(sw1.ElapsedTicks);
//21527242

//2回に分けて連結する(4,1に分ける)
System.Diagnostics.Stopwatch sw2 = new System.Diagnostics.Stopwatch();
sw2.Start();
for (int i = 0; i < 50000000; i++)
{
    string r2 = s1 + s2 + s3 + s4;
    r2 += s5;
}
sw2.Stop();
Console.WriteLine(sw2.ElapsedTicks);
//14886505

//2回に分けて連結する(3,2に分ける)
System.Diagnostics.Stopwatch sw3 = new System.Diagnostics.Stopwatch();
sw3.Start();
for (int i = 0; i < 50000000; i++)
{
    string r3 = s1 + s2 + s3;
    r3 += s4 + s5;
}
sw3.Stop();
Console.WriteLine(sw3.ElapsedTicks);
//13423224

一度に連結した方が速くなる文字列の数は?

ある程度の数の文字列ならば、一度に連結するよりも分けて連結した方が速くなることは分かりましたが、数が多くなれば一度に連結した方が速くなるはずです。そこで、幾つ連結すれば一度に連結した方が速くなるか調べてみました。すると、私の環境では(.NET Framework 4.0)、大体14個以上の文字列を連結した時、一度に連結した方が速くなりました。

VB.NET
コードを隠すコードを選択
Dim s1 As New String("あ"c, 10)
Dim s2 As New String("い"c, 10)
Dim s3 As New String("う"c, 10)
Dim s4 As New String("え"c, 10)
Dim s5 As New String("お"c, 10)

Dim sw1 As New System.Diagnostics.Stopwatch()
sw1.Start()
For i As Integer = 0 To 50000000
    Dim r1 As String = s1 & s2 & s3 & s4 & s5 & s1 & s2 & s3 & s4 & s5 _
                       & s1 & s2 & s3 & s4
Next
sw1.Stop()
Console.WriteLine(sw1.ElapsedTicks)
'52284326

Dim sw2 As New System.Diagnostics.Stopwatch()
sw2.Start()
For i As Integer = 0 To 50000000
    Dim r2 As String = s1 & s2 & s3 & s4
    r2 += s5 & s1 & s2
    r2 += s3 & s4 & s5
    r2 += s1 & s2 & s3
    r2 += s4
Next
sw2.Stop()
Console.WriteLine(sw2.ElapsedTicks)
'53227035
C#
コードを隠すコードを選択
string s1 = new string('あ', 10);
string s2 = new string('い', 10);
string s3 = new string('う', 10);
string s4 = new string('え', 10);
string s5 = new string('お', 10);

System.Diagnostics.Stopwatch sw1 = new System.Diagnostics.Stopwatch();
sw1.Start();
for (int i = 0; i < 50000000; i++)
{
    string r1 = s1 + s2 + s3 + s4 + s5 + s1 + s2 + s3 + s4 + s5 +
        s1 + s2 + s3 + s4;
}
sw1.Stop();
Console.WriteLine(sw1.ElapsedTicks);
//52284326

System.Diagnostics.Stopwatch sw2 = new System.Diagnostics.Stopwatch();
sw2.Start();
for (int i = 0; i < 50000000; i++)
{
    string r2 = s1 + s2 + s3 + s4;
    r2 += s5 + s1 + s2;
    r2 += s3 + s4 + s5;
    r2 += s1 + s2 + s3;
    r2 += s4;
}
sw2.Stop();
Console.WriteLine(sw2.ElapsedTicks);
//53227035

なお、環境によって結果が変わる可能性があることをご了承ください。

  • 履歴:
  • 2011/4/12 パフォーマンスに関する説明を追加。String.Formatメソッドに関する説明を追加。
  • 2011/5/7 「文字列の配列を連結する」を追加。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • .NET Tipsをご利用いただく際は、注意事項をお守りください。