Split関数は区切り文字が1つしか使えない
Split関数は文字列の中にある区切り文字で分割して配列にする関数です。
“aaa,bbb”という文字列を、カンマ文字で分割して、”aaa”と”bbb”にするような用途で使います。
Split関数の詳細については「VBA関数:指定文字で分割して配列にする(Split)」をご参照ください。
このSplit関数ですが、区切り文字の指定は1種類しかできません。そのため、2種類以上の区切り文字を使いたい場合にはそのままでは使えないため、別の方法を考える必要があります。
Split関数を区切り文字の種類の数だけ繰り返すと問題が起きる
区切り文字を複数使いたいのであれば、その区切り文字の種類の数だけ、ループしてSplit関数を実行するのも1つの方法です。
ただ、その場合には2つのことを考慮する必要があります。
1つ目は区切り文字の数だけ全体のループ回数が増えるという点で、再帰、という考え方が必要になります。
2つ目は分割したあとに配列に格納される順番です。こちらは1つ目よりもさらに問題です。
何が問題かを例で示します。
以下のようにスラッシュ(/)とアスタリスク(*)を持つ文字列があるとします。見やすいように空白文字を入れてますが空白文字はないものとして考えてください。
“AAAA / BBBB * CCCC / DDDD * EEEE”
これを、/と*のそれぞれでSplit関数で分割します。
まず、/で分割すると、
“AAAA”
“BBBB * CCCC”
“DDDD * EEEE”
の3つに分割されます。
そのあとに3つに分割された文字列それぞれに対して *文字 で分割します。
* で分割すると、
“AAAA”
“BBBB * CCCC” → ”BBBB”と”CCCC”
“DDDD * EEEE” → ”DDDD”と”EEEE”
のようになりますが、では、分割後の”CCCC”と”EEEE”は分割後の配列のどこに格納するのが正しいでしょうか。
普通に考えて期待するのは、元の文字列の左から順に区切ったごとに配列に格納してほしいと考えます。
“AAAA”
“BBBB”
“CCCC”
“DDDD”
“EEEE”
しかし、Split関数は配列を返し、それをループで回して再度Split関数で分割し、さらに・・・ということを区切り文字の種類の数だけ繰り返すと、最終的に格納する配列に正しく期待した順番に入れるには、既に作成済みの配列の途中に、新たに別の要素を入れるにはそれ以降の要素の内容をずらす必要があったり、そうでなくてもそれに近い考慮をしてやる必要が出てくるため、実は結構面倒になることが分かります。
そこで、以下に上記の問題の2つともを考慮した上で、複数の区切り文字を指定できるSplit関数の拡張版を紹介します。
Split関数の拡張版関数:SplitEx
以下のSplitEx関数は、使い方はSplit関数とほとんど同じです。
Split関数には「元文字列、区切り文字、分割数、あいまい検索」の4つの引数がありますが、分割数(Limit)とあいまい検索(Compare)の2つはほとんど使うことがないため、以下のSplitEx関数では削除してます。
構文は以下になります。2つありますがどちらでも構いません。違いは第二引数の書き方だけです。
配列変数 = SplitEx(元文字列, 文字列配列変数)
配列変数 = SplitEx(元文字列, Array(“区切り文字1”, “区切り文字2″, ・・・,”区切り文字n”))
第一引数はSplit関数と同様で元文字列を指定します。第二引数には文字列配列かArray関数での文字列配列を渡します。区切り文字の種類はいくつあっても構いません。
分割後の配列は、元の文字列の左から順番に配列に格納されるようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
Function SplitEx(Expression, ParamArray Delimiter() As Variant) As Variant Dim v As Variant '// 配列要素 Dim i As Integer '// ループカウンタ Dim s As String '// 引数文字列 Dim sFirst As String '// 配列先頭要素の区切り文字 '// 文字列型ではない場合 If TypeName(Expression) <> "String" Then GoTo ERROR_EXIT End If '// 引数文字列をコピー s = Expression '// 引数文字列が未設定の場合 If s = "" Then GoTo ERROR_EXIT End If '// ループカウンタを初期化 i = 0 '// 区切り文字配列をループ For Each v In Delimiter(0) '// 文字列型ではない場合 If TypeName(v) <> "String" Then GoTo ERROR_EXIT End If '// ループ初回時 If i = 0 Then '// 初回区切り文字を保持 sFirst = v End If '// 引数文字列の区切り文字を初回区切り文字に置き換え s = Replace(s, v, sFirst) '// ループカウンタを加算 i = i + 1 Next '// 区切り文字が未設定の場合 If sFirst = "" Then GoTo ERROR_EXIT End If '// 置換 SplitEx = Split(s, sFirst) Exit Function ERROR_EXIT: '// 戻り値をなしにする SplitEx = Empty End Function |
コメントに大体書いていますので処理内容はそちらを見てもらうとして、考え方について補足すると、引数で渡された区切り文字配列の先頭要素の区切り文字を使いまわすようにしています。
引数の元文字列に対して、区切り文字配列の2番目以降の文字があった場合、それを先頭の区切り文字に一旦置き換えます。
例えば、元文字列の、
「”AAAA / BBBB * CCCC / DDDD * EEEE”」
に対して / と * の2つを区切り文字としている場合は、1つ目の/で2つ目の*を置き換えて、元の文字列の区切り文字の種類を1種類に整理します。
「”AAAA / BBBB / CCCC / DDDD / EEEE”」
そのあとに、その1種類の区切り文字でSplit関数を実行しています。これによりSplit関数が1度しか実行されないことから、元文字列の左から順に配列に格納されるようになっています。
途中でGoToが何か所かありますが、引数のデータ型がString型でない場合は不正引数とみなして処理をしないようにしています。引数がすべてString型で正常であればSplit関数を実行してそれを関数の戻り値として関数を終了しています。
使い方
4つのパターンで使い方を書いてます。
サンプルコードにある「Debug.Print “区切り文字は」の行で各パターンが始まります。
1つ目のパターンは引数に渡す文字列配列を事前に用意した場合で、2つ目はArray関数を使う場合、3つ目が元文字列に含まれない区切り文字を指定した場合、4つ目が文字列でない引数を渡した場合です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
Sub SplitExTest() Dim s As String Dim v Dim i Dim ar() ReDim ar(1) ar(0) = "," ar(1) = "@" s = "1,2-3@4@5,6789,10" Debug.Print "区切り文字は[, @]" '// 区切り文字に [, @] の2文字を指定 v = SplitEx(s, ar) '// 正常に分割できた場合 If IsEmpty(v) = False Then '// 分割結果を出力 For i = 0 To UBound(v) Debug.Print v(i) Next End If Debug.Print "区切り文字は[, - @]" '// 区切り文字に [, - @] の3文字を指定 v = SplitEx(s, Array(",", "-", "@")) '// 正常に分割できた場合 If IsEmpty(v) = False Then '// 分割結果を出力 For i = 0 To UBound(v) Debug.Print v(i) Next End If Debug.Print "区切り文字は[2 @ , *]" '// 区切り文字に [2 @ , *] の4文字を指定 v = SplitEx(s, Array("2", "@", ",", "*")) '// 正常に分割できた場合 If IsEmpty(v) = False Then '// 分割結果を出力 For i = 0 To UBound(v) Debug.Print v(i) Next End If Debug.Print "区切り文字は[*, 0, Range(A1)]" '// 区切り文字に [* 0 Rangeオブジェクト] の3種類を指定 v = SplitEx(s, Array("*", "0", Range("A1"))) '// 正常に分割できた場合 If IsEmpty(v) = False Then '// 分割結果を出力 For i = 0 To UBound(v) Debug.Print v(i) Next Else Debug.Print "*, 0, Range(A1) = Empty" End If End Sub |
実行結果
区切り文字は[, @]
1
2-3
4
5
6789
10
区切り文字は[, – @]
1
2
3
4
5
6789
10
区切り文字は[2 @ , *]
1
-3
4
5
6789
10
区切り文字は[*, 0, Range(A1)]
*, 0, Range(A1) = Empty