LoginSignup
16
17

More than 5 years have passed since last update.

Excel/VBAで正規表現検索するラップ関数

Posted at

VBAでの正規表現ラップ関数

VBAで正規表現を使う場合はVBScript.RegExpオブジェクトを呼び出すのが常套らしいですが、くっそ使いづらいのでラップ関数を作りました。

'  String(n)を返す。n = 0 にはマッチした文字列全体、n >= 1 にはExpressionの"()"内にマッチした文字列が入る。
'  マッチしない場合は String(0) = vbNullString を返す。
'  GlobalMatch = True で呼び出した後、引数を与えずに呼び出すと、2つ目以降のMatchDataを順に返す。
Function RegMatchStrings(Optional Expression As String, Optional SearchTarget As String, Optional GlobalMatch As Boolean = False) As String()

    Dim objRawRegExp As Object
    Dim ret() As String

    Static matches As Object
    Dim match As Object
    Dim submatches As Object

    Static mi As Long
    Dim i As Long

    If Expression = vbNullString And SearchTarget = vbNullString Then
        mi = mi + 1
        If Not matches Is Nothing Then
            If mi > matches.Count - 1 Then Set matches = Nothing
        End If
    Else
        Set objRawRegExp = CreateObject("VBScript.RegExp")
        objRawRegExp.Global = GlobalMatch
        objRawRegExp.Pattern = Expression
        If objRawRegExp.Test(SearchTarget) Then
            Set matches = objRawRegExp.Execute(SearchTarget)
        Else
            Set matches = Nothing
        End If
        mi = 0
    End If

    If Not matches Is Nothing Then
        Set match = matches(mi)

        ReDim ret(0 To match.submatches.Count)
        ret(0) = match

        If match.submatches.Count > 0 Then
            For i = 0 To match.submatches.Count - 1
                ret(1 + i) = match.submatches(i)
            Next i
        End If
    Else
        ReDim ret(0 To 0)
        ret(0) = vbNullString
    End If

    RegMatchStrings = ret
End Function

こんな感じで使えます。

sample
Sub Test()
    Dim rms() As String
    Dim s As Variant
    rms = RegMatchStrings("(\w*)-(\d{8})\.jpe?g", "image-20140227.jpeg, picture-20140228.jpg", True)
    Do While rms(0) <> vbNullString
        For Each s In rms
            Debug.Print s
        Next s
        rms = RegMatchStrings
    Loop
End Sub
result
image-20140227.jpeg
image
20140227
picture-20140228.jpg
picture
20140228

結果が全てStringで返ってくるので、RegExp.TestとかIS Nothingとかで調べなくていいのが楽だと思います。

Excel関数で正規表現検索

上記のラップ関数はそのままExcelのワークシート関数として呼び出しても使えますが、流石に使いづらいので更にラップで包みます。

' for EXCEL Worksheet Function
'
' マッチしたセルの相対的な位置を返す。
Function REGMATCH(Expression As String, SearchRange As Range) As Variant
    Application.Volatile
    Dim Cell As Range
    Dim i As Long

    REGMATCH = CVErr(xlErrNA)
    i = 1
    For Each Cell In SearchRange
        If RegMatchStrings(Expression, Cell.value, False)(0) <> vbNullString Then
            REGMATCH = i
            Exit For
        End If
        i = i + 1
    Next
End Function
' for EXCEL Worksheet Function
'
' マッチした文字列の配列を返す。
' MatchIndexに値を渡すと、対応した部分文字列を返す。
' マッチしない場合は #VALUE! エラーを、MatchIndexが範囲外の場合は #N/A! エラーを返す。
Function REGSEARCH(Expression As String, SearchTarget As String, Optional MatchIndex As Long = 0) As Variant
    Application.Volatile
    Dim rms() As String

    rms = RegMatchStrings(Expression, SearchTarget)
    If rms(0) <> vbNullString Then
        If MatchIndex = 0 Then
            REGSEARCH = rms
        Else
            If MatchIndex <= UBound(rms) Then
                REGSEARCH = rms(MatchIndex)
            Else
                REGSEARCH = CVErr(xlErrNA)
            End If
        End If
    Else
        REGSEARCH = CVErr(xlErrValue)
    End If
End Function

それぞれ、Excel関数のMATCH()、SEARCH()に挙動を合わせています。
ただし、SEARCH()はヒットした文字列の位置を返しますが、REGSEARCH()はヒットした文字列のそのものを返すようにしています。


正規表現検索するだけでこんな糞長いコード書かなければいけないのでExcel VBAは糞だと思いました。

16
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
17