和風スパゲティのレシピ

日本語でコーディングするExcelVBA

文字列内の指定文字の登場回数をカウントする

文字列の中に、ある文字がいくつ入っているかを調べる方法を解説します。

「AA,B,CCC,DD」の中にカンマがいくつあるかを数えるコードですね。

あまりない処理ですが、「みかんいちごみかんりんごみかん」の中に「みかん」がいくつあるかという、文字「列」の登場回数のカウントも同じコードで実装できます。

最少コード量のSplit関数法

最もコード量が少なくて済むのが、Split関数を使う方法です。

登場回数 = Ubound(Split(テキスト全体, 探す文字列))

' 実行例
MsgBox Ubound(Split("AA,B,CCC,DD", ",")) ' ← 3が表示されます。

このようにとても短いコードで実装できます。


Split関数は文字列を指定文字で分割した配列を返す関数で、
丁寧にコードを書くと、

Dim テキスト全体 As String
テキスト全体 = "AA,B,CCC,DD"
Dim 配列
配列 = Split(テキスト全体, 探す文字列)

' この段階で「配列」は、[AA][B][CCC][DD]の4つの文字列を格納

' 登場回数 = 配列の要素数 - 1
Dim 登場回数 As Long
登場回数 = Ubound(配列) + 1 - 1

こういう仕組みです。

最後の式の「配列」にその中身を代入すれば、

登場回数 = Ubound(Split(テキスト全体, 探す文字列))

この式になりますね。


Uboundは配列の添字の上限を求める関数ですが、
配列は要素番号が「0」から始まるので、要素数はUBound+1です。

+1-1できれいに消えるのが気持ちいいですね。

ワークシートでも使えるReplace関数法

続いてReplace関数を使う方法です。

登場回数 = (Len(テキスト全体) - Len(Replace(テキスト全体, 検索値, ""))) / Len(検索値)

式が長いのでわかりづらいですが、

  1. みかんいちごみかんりんごみかん の文字数は15
  2. そこからみかんを消した いちごりんご の文字数は6
  3. 減った文字数は15-6 = 9
  4. 減った9文字は3文字のみかん3回分

という手順で求めています。


探す文字がカンマのように1文字の場合は、最後の割り算が1で消えるので、

登場回数 = Len(テキスト全体) - Len(Replace(テキスト全体, 探す1文字, ""))

と、ちょっと短くなります。


Split関数の方法を知った今、こっちをわざわざVBAで使う必要はありませんが、

A B
みかんいちごみかんりんごみかん みかん

このときに、

C1 =(LEN(A1)-LEN(SUBSTITUTE(A1,B1,"")))/LEN(B1)

と、この方法はワークシート上でも使えるいうメリットがありますので、
覚えておいて損はないと思います。


というかこんな式組むの面倒だからSplitをシート関数にしてほしいんだけど(´∀`;)

処理最速のInstr関数法

上記のような便利な関数やテクニックを使用せず、
愚直に左から探していくコードがこちらです。

Sub 文字列の登場回数をカウントする()
    Dim 登場回数 As Long
    Dim テキスト全体 As String: テキスト全体 = "みかんいちごみかんりんごみかん"
    Dim 検索値 As String: 検索値 = "みかん"
    Dim 検索値の長さ As Long: 検索値の長さ = Len("みかん")

    Dim 検索値の位置 As Long: 検索値の位置 = InStr(テキスト全体, 検索値)
    If 検索値の位置 = 0 Then
        登場回数 = 0
        Exit Sub
    End If
    
    Dim 現登場回数 As Long: 現登場回数 = 1
    Do
        検索値の位置 = InStr(検索値の位置 + 検索値の長さ, テキスト全体, 検索値)
        If 検索値の位置 = 0 Then
            Exit Do
        Else
            現登場回数 = 現登場回数 + 1
        End If
    Loop

    登場回数 = 現登場回数
End Sub

長いですが、丁寧に変数名つけましたので、じっくり読めばわかると思います。
解説は不要というか、コード本体以上の解説を文章で書けません。


注意点を補足すると、

検索値の位置 = InStr(検索値の位置 + 検索値の長さ, テキスト全体, 検索値)

ここで、「前に見つかった検索値の次の検索値」を探していますが、
これを、

検索値の位置 = InStr(検索値の位置 + 1, テキスト全体, 検索値)

としてしまうと、「すもももももももものうち」から「もも」をカウントするときに、
7が答えとして返ってくるバグを生みますのでお気を付けください。


このInstr関数で左から探す方法は最速です。

上記2つの方法の、数倍のスピードが出ます。


まあ上記2つは無駄に配列を作ったり置換をして、目的の文字以外の文字たちも処理しているわけですから、当然といえば当然ですね。


ただし「コードを書く時間」もコストですので、
この方法はトータルで早いマクロとは言えないですし、

真に処理速度を求められるマクロだったとしたら、
この処理だけ数倍のスピードにしても焼け石に水です。

(検索とかセルへの出力とか、もっと時間のかかる処理を高速化しないといけないので、文字列処理は高速化の優先順位が低い)


ということで、本当にこの速度を生かすとしたら、
速度をそのままに次からはこのコードを書かなくて済むよう、
自作の関数にする必要があります。

↑上のコードをちょっと書き換えるだけなので作れる関数ですので、
Functionプロシージャを使ったことがない人も見てみてください。

Function Count文字列の登場回数(ByVal テキスト全体 As String _
                                            , ByVal 検索値 As String) As Long
    Dim 検索値の長さ As Long: 検索値の長さ = Len(検索値)

    Dim 検索値の位置 As Long: 検索値の位置 = InStr(テキスト全体, 検索値)
    If 検索値の位置 = 0 Then
        Count文字列の登場回数 = 0
        Exit Function
    End If
    
    Dim 現登場回数 As Long: 現登場回数 = 1
    Do
        検索値の位置 = InStr(検索値の位置 + 検索値の長さ, テキスト全体, 検索値)
        If 検索値の位置 = 0 Then
            Exit Do
        Else
            現登場回数 = 現登場回数 + 1
        End If
    Loop

    Count文字列の登場回数 = 現登場回数
End Function
' 関数の使用例(2が表示されます)
MsgBox Count文字列の登場回数("みかんりんごみかんいちご","みかん")

これで次からは「Count文字列の登場回数」と書くだけでこの高速関数を使えます。


中身と仕組みがわかりやすい関数なので、関数化の参考になると思いますし、
勿論中身を見ずにコピペで持って帰って使ってもOKです。


しかもFunctionでつくられた自作関数は、

A B
みかんいちごみかんりんごみかん みかん

このときに、

C1 =Count文字列の登場回数(A1,B1)

と、シート関数として使うこともできます。


Splitの使えないシートでこれを使えるというのはかなりのメリットですよね。


最初のSplit法を読んだ時点でブラウザの戻るボタンを押してもいいところを、
こんな奥地まで読み込んでくれた酔狂なあなたに、この関数をプレゼントします。

参考にするなり、中身を見ないでコピペして使うなり、
お好きなようにお使いください。


関数づくりを勉強したいという方は、こちらの記事もどうぞ。
www.limecode.jp