HOME > 即効テクニック > Excel VBA > 文字列操作関連のテクニック > 文字列を高速に連結する(Midステートメント)

文字列を高速に連結する(Midステートメント)|Excel VBA

文字列操作関連のテクニック

文字列を高速に連結する(Midステートメント)

(Excel 2000/2002/2003/2007/2010/2013/2016)

Midステートメントは文字列の一部を入れ替える命令です。
次の構文のように、変数stringvarの一部をstringで指定した文字列に置き換えます。

同じ名前でMid関数という関数がありますが、Mid関数は文字列の中から任意の文字列を取り出して返します。名前が同じで紛らわしいですが、構文が異なります。

構文 Mid(stringvar, start, length) = string
設定項目 内容
stringvar 変更する文字列の変数名を指定[省略不可]
start 置換開始位置を指定[省略不可]
length 置換する文字数を指定[省略可]
string 置換する文字列を指定[省略不可]

■文字列を置換する

次のサンプルはMidステートメントを使って文字列を置換する例です。

●サンプル1●

Sub Sample1()
    Dim Msg As String
    
    Msg = "123456789"
    Mid(Msg, 3, 1) = "moug"
    Debug.Print Msg '→ 12m456789  3文字目から1文字だけ置換する

    Msg = "123456789"
    Mid(Msg, 3) = "moug"
    Debug.Print Msg '→ 12moug789  引数lengthを省略するとすべて置換する

    Msg = "123456789"
    Mid(Msg, 1) = "www.moug.net"
    Debug.Print Msg '→ www.moug.  10文字目以降が切れる
        
    Msg = "123456789"
    Mid(Msg, 3) = "モーグ"
    Debug.Print Msg '→ 12モーグ6789  全角文字の場合
End Sub

ただし、変数Msgの長さが9文字のとき、引数startに10以上を指定するとエラーになります。

    Msg = "123456789"
    Mid(Msg, 10) = "www.moug.net" '→ エラー

■文字列を連結する

文字列を連結するときは、次のコードのように&演算子を使用します。

Msg = "モーグ" & "即効" & "テクニック"

&演算子のかわりに+演算子を使うこともできますが、文字列が数字のときなど、思わぬ結果になることもあるので+演算子は使わない方が良いでしょう。

文字列の連結は、上のコードがもっとも一般的な方法ですが、Midステートメントで文字列を連結することもできます。このとき、連結後の文字列の長さの分だけ、あらかじめ変数の領域を確保しておくことがポイントです。

●サンプル2●

Sub Sample2()
    Dim Msg As String
    Msg = Space(10)
    Mid(Msg, 1) = "モーグ"
    Mid(Msg, 4) = "即効"
    Mid(Msg, 6) = "テクニック"
    MsgBox Msg
End Sub

文字列をカンマで区切って連結したり、改行しながら連結させたいといったケースもよくあります。

Msg = "モーグ" & "," & "即効" & "," & "テクニック"

この場合は、あらかじめ変数Msgに文字数分のカンマ「,」や改行コードをいれておいてからMidステートメントで連結すると、上のコードと同じ結果が得られます。

●サンプル3●

Sub Sample3()
    Dim Msg As String
    Msg = String(12, ",")
    Mid(Msg, 1) = "モーグ"
    Mid(Msg, 5) = "即効"
    Mid(Msg, 8) = "テクニック"
    MsgBox Msg
End Sub

■連結方法による処理速度の比較

文字列の連結をするのに「あらかじめ連結後の文字列領域を確保しておく」のが面倒ですが、Midステートメントによる文字列の連結は&演算子による連結よりも高速である、というメリットがあります。

50kb級以上の長い文字列を&演算子で文字列を連結する場合、内部で次のような処理が行われます。

  1. 連結後の文字列を格納するための一時領域を、メモリ上に確保
  2. 一時領域の先頭に元の文字列をコピー
  3. 一時領域の元の文字列の後ろに、連結する文字列をコピー
  4. 元の文字列のメモリ領域を解放
  5. 連結後の文字列を格納するためのメモリ領域を確保
  6. 一時領域の(連結済みの)データを5)の領域にコピー

上記の2)と6)で元の文字列をコピーするため、繰り返しコピーするデータサイズが大きくなるほど処理時間は長くなります。

これに対し、Midステートメントの方は連結後の文字列の領域を一度だけ確保し、その領域内のデータを上書きしていきます。
(参考:Microsoftサポート)

では、どのくらい処理速度が異なるのか比較してみましょう。
サンプル4は&演算子を使って文字列を連結します。

●サンプル4●

Sub ConcatinateWithAmpersand(N As Long)
    Dim Msg As String
    Dim Data As Variant
    Dim s As Variant

    Data = Range(Cells(1, 1), Cells(N, 1)).Value '---(1)
    
    For Each s In Data    
        Msg = Msg & s
    Next s
End Sub

サンプル5はMidステートメントを使って文字列を連結します。

●サンプル5●

Sub ConcatinateWithMid(N As Long)
    Dim Msg As String
    Dim Data As Variant
    Dim L As Long
    Dim s As Variant
    Dim pt As Long
    
    Data = Range(Cells(1, 1), Cells(N, 1)).Value '---(1)
    
    '連結後の文字列長を求める
    For Each s In Data
        L = L + Len(s)
    Next s
    
    '変数Msgのスペースを確保する
    Msg = Space(L)
    
    pt = 1  '文字位置
    For Each s In Data
        Mid(Msg, pt, Len(s)) = s
        pt = pt + Len(s)
    Next s
End Sub

アクティブシートのセルA1:A50000には「モーグ」の文字列が入力されています。
どちらのサンプルも引数Nを受け取ります。
(1)で参照するセル範囲を変えることにより、連結回数を変化させて計測しました。

連結回数が多くなるほど差は顕著になっています。
ここで&演算子による連結の、処理時間の増え方に注目してみます。

1,000回  0.972ミリ秒  →   5,000回   5.031ミリ秒
1,000回  0.972ミリ秒  →  10,000回  20.546ミリ秒

連結回数5倍で時間もおよそ5倍、連結回数10倍でおよそ20倍です。

3,000回  1.801ミリ秒  →  30,000回  184.153ミリ秒
5,000回  5.031ミリ秒  →  50,000回  608.984ミリ秒

連結回数10倍で、時間が100倍以上になっています。

&演算子で50kb級以上の大きな文字列を連結すると、一時領域を用意することで文字列のコピーが2回行われます。「モーグ」は6バイトなので、連結後の文字列が50kb以上になるのは、計算上は8334回目で、上の表で赤線になっているあたりから連結の際に2回のコピーが行われていることになります。
他の要因もあるので、正確に、50kb以下は文字列の長さに比例、50kb以上は長さのべき乗に比例というわけにはいきませんが、1つの目安として頭の片隅に入れておくといいかもしれません。

これをグラフにすると次のようになります。

連結回数が少なく文字列サイズが小さいうちは、その差はあまり大きくありませんが、長い文字列を何度も連結するようなケースでは、Midステートメントの方が圧倒的に有利ですね。
ケースに応じて使い分けるとよいでしょう。

●補足1●

どちらのサンプルも(1)のコードでは指定したセル範囲の値(Valueプロパティの値)をバリアント型の配列に代入しています。このコードを

Set Data = Range("A1:A50000")

のようにSetステートメントを使ってオブジェクト変数に代入してしまうと、以降の処理でRangeオブジェクトに何度もアクセスすることになり、非常に遅くなるので注意してください。

●補足2●

長い文字列の連結はMidステートメントが高速ですが、可読性の点では&演算子による結合の方が直観的でわかりやすいですね。
そこで次善の策として、カッコをつける方法をご紹介します。
次のように短い文字列部分の連結を先に行うことで、長い文字列の連結回数を減らすことになり、処理速度を向上できます。

Msg = Msg & "最後まで" & "読んでくれて" & "ありがとう!"

    ↓

Msg = Msg & ("最後まで" & "読んでくれて" & "ありがとう!")