[[20250414192856]] 『VBA 正規表現について』(TKG) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) |

| 全文検索 | 過去ログ ]

 

『VBA 正規表現について』(TKG)

 ものすごく初歩的な質問です。温かい目でご回答いただけると幸甚です。
 VBAの正規表現についてですが、例えば「17uehdbi72637ug63hv」のように
 数値と文字が混合している文字列において、「数値と判定できる部分」と「それ以外の部分」で分割する場合、例えば「17」「uehdbi」「72637」「ug」「63」「hv」と分割する場合、
 どのような正規表現を使えば一発で分割できるのでしょうか?
 例の文字列は適当に打っただけなのでなんの意味もありませんが、分割後は6つになるとは限らず、不特定多数としていただきたいです。
 IsNumericとMidで1文字ずつ判定してゴリ押しで対応していましたが、あまりにも冗長なので質問させていただきました。 Functionでもサブプロシージャでも構いません。
 無知な私にご教示くださいませ.

< 使用 Excel:Microsoft365、使用 OS:Windows11 >


 加えて図々しいお願いですが、「こんな使い方もあるよ」も教えていただければ幸いです。
(TKG) 2025/04/14(月) 19:36:47

 例えば
 =TEXTSPLIT(REGEXREPLACE(A1,"(\d)(\D)|(\D)(\d)","$1$3^$2$4"),"^")
(jindon) 2025/04/14(月) 20:33:30

 VBA でしたか...
 いずれ使用できなくなりますが...

 Function mySplit(s$)
    With CreateObject("VBScript.RegExp")
        .Global = True
        .Pattern = "(\d)(\D)|(\D)(\d)"
        mySplit = Split(.Replace(s, "$1$3^$2$4"), "^")
    End With
End Function
(jindon) 2025/04/14(月) 20:39:03

 単純な方法も示しておきましょう。 
 Sub test()
     Dim re      As Object
     Dim al      As Object
     Dim matches As Object
     Dim m       As Object
     Dim s       As String

     Set re = CreateObject("VBScript.RegExp")
     Set al = CreateObject("System.Collections.ArrayList")
     With re
         .Global = True
         .Pattern = "\d+|\D+"
     End With

     s = "17uehdbi72637ug63hv"

     Set matches = re.Execute(s)
     For Each m In matches
         al.Add m.Value
     Next
     Debug.Print Join(al.ToArray, ",")
 End Sub

(xyz) 2025/04/14(月) 21:03:39


 A1に 17uehdbi72637ug63hv とあるとします。もしも
B1
=REGEXEXTRACT(A1,"\d+|\D+",1)
 でうまくいく環境であれば、これをVBAで利用して
Sub test()
  RegExt Range("A1"), "\d+|\D+", Range("B1"), 1
End Sub
Sub RegExt(Rng1 As Range, p As String, Rng2 As Range, Optional n As Long = 0)
  Dim v
  v = Evaluate("REGEXEXTRACT(""" & Rng1.Value & """,""" & p & """," & n & ")")
  If IsError(v) Then Exit Sub
  If n = 0 Then
    Rng2.Value = v
  Else
    Rng2.Resize(1, UBound(v)).Value = v
  End If
End Sub

(んなっと) 2025/04/14(月) 21:33:22


 Option Explicit
Sub main()
    Dim pstr(), x(), tx$, vAr, rg, i&, res
    Set rg = CreateObject("VBScript.RegExp")
    tx = "17uehdbi72637ug63hv1b1b3b"
    tx = ""
    pstr = Array("[^0-9]*", "[0-9]*")
    For Each vAr In pstr
        If ah(vAr, x, tx, rg) Then
            For i = LBound(x) To UBound(x)
                res = res & x(i) & Chr(13)
            Next
            Erase x
        End If
    Next
    Erase pstr, x
    Set rg = Nothing
    If Len(res) > 0 Then
        MsgBox res
    Else
        MsgBox "見つかりませんでした"
    End If
End Sub
Private Function ah(pstr, x, tx$, rg) As Boolean
    Dim md, i&, vAr, xx()
    With rg
        .Pattern = pstr
        .IgnoreCase = False
        .Global = True
    End With
    Set md = rg.Execute(tx)
    For Each vAr In md
        If vAr.Value <> "" Then
            ReDim Preserve xx(i)
            xx(i) = Trim(vAr.Value)
            i = i + 1
        End If
    Next
    If i > 0 Then ah = True
    x = xx
    Erase xx
End Function

冗長な書き方。。。^^; わたし、あまり詳しくないので 後方とか前方とかグループ等々は
良く理解出来ておりません。。。諸先輩の後で恐縮ですが、お勉強がてら、書いてみましたので
研究発表〜でぇ〜す (*^^*)
m(__)m
(隠居Z) 2025/04/15(火) 11:31:22


 >jindon様
 ありがとうございます。こんな単純に書けてしまうものなんですね。
 "VBScript.RegExp"が近々参照不可になるということでしょうか?
 VBEの開発は終わってるという話は聞いたことがありますがVBA自体の仕様変更はこの先も起こるんですかね
 無知な質問で申し訳ないのですが、"$1$3^$2$4"はどういった意味なのでしょうか?

 >xyz様
 ありがとうございます。やはり正規表現にしたところでFor Eachで取り出すのが単純な使い方でしょうか。
 ArrayList、恥ずかしながら初見でした。意外と使い所がありそうですね。
(TKG) 2025/04/15(火) 14:29:57

 >んなっと様
 ありがとうございます。恥ずかしながらワークシート関数でもほとんど用いたことがありません笑
 Evaluateで呼び出してしまった方が全体の記述量としては削減できますね。

(TKG) 2025/04/15(火) 14:43:33


 (1)
 >どのような正規表現を使えば一発で分割できるのでしょうか?
 既にお気づきのように、
 "(\d+|\D+)+"といったパターンマッチが思いつきますが、
 カッコによる部分文字列の取得は、容れ物がひとつなのでマッチのつど上書きされ、
 最後のマッチによるものしか取り出せません。
 もちろん(\d+|\D+)(\d+|\D+)(\d+|\D+)・・・のように陽に記述すれば、Submatchesで取り出せますが、
 可変個という制約をクリアーすることはできません。

 従って、
         .Global = True
         .Pattern = "\d+|\D+"
 などとして、matchesコレクションの中を見ていくことになります。
 マッチ文字列を配列で返す機能はないので、配列を自力で構成せざるを得ません。
 それが私が提示したものです。
 (ArrayListを使ったのは、VB6の配列はRedim処理が面倒(*)なことが理由です。
   pythonのappendや、JavascriptやRubyのpushのようなものはVBAにはありません.)

 jindonさんが示されたように数字と数字以外の境目に着目して
 巧妙にReplaceを使用する方法もあります。

 (2)
 そもそもですが、VBAに限定されていますが、ユースケースによるでしょう。
 どのような場面で正規表現を必要としているのでしょうか。

 最近の365環境であれば正規表現関連の3関数が使えます。
 (全ての365が使えるわけではありません。環境依存です)
 # 足元に使える(かもしれない)関数が登場していた、という感じです。

 んなっとさんが示されたように、
 =REGEXEXTRACT(A1,"\d+|\D+",1)
 とすることで、A1セルの文字列を分解した文字列の配列が得られます。

 ワークシートに関数を作っておいて、
 ワークシートをユーザー定義関数のように見立てて利用することもできるでしょう。
     Dim v
     Sheet1.[A1] = "17uehdbi72637ug63hv"
     v = Evaluate("Sheet1!B1#")
     v = Application.Transpose(Application.Transpose(v)) '1次元配列

 VBScriptは時期ははっきりとは確定していませんが、将来的に廃止されることが決まっています。
 ですから、上記のようなワークシート関数の利用も対応策のひとつになるかもしれません。

(xyz) 2025/04/15(火) 14:51:06


 TKGさん
 VBAでの正規表現はいずれ使用できなくなります。
 私も多用してきたので、書き換えています。

 私はVBAでの処理を基にして関数を作成しています。
 見てわかる通り、私が投稿した関数とVBAのパターンは同じです。

 VBAでの正規表現の一番のメリットはコードが短縮できることです。
 が、あくまで私の経験からですが、処理速度は遅くなります。

 VBAでの文字列操作はメモリマネージメントをきちんとすれば、とても速いです。
 ですので、"ゴリゴリ"の処理を無駄にしないでください。

(jindon) 2025/04/15(火) 15:23:25


 PY関数を使って見るテスト

 =PY( のあと、以下のコードを入力して CTRL+Enter
 import re
 re.findall('\d+|\D+',xl("A1"))

 セルの右クリックして、Python出力を Excelの値 に

 私のところ、PY関数は使えるけど、REGEXEXTRACT系の関数がまだ使えません
(´・ω・`) 2025/04/15(火) 15:57:13

 おや、そうですか企業版?ですか?

 pythonの話で一応、すべての道具が網羅されましたでしょうかね。
 あとは正規表現を使わない版でしょうか。

 ちなみに、私もpythonやってみましたけど、それだと縦ベクトルが返りますね。(正確にはlistでしょうけど)
 [re.findall('\d+|\D+',xl("A1"))]
 と書くと横ベクトルになるようで、すぐには思いつきませんでした。
(xyz) 2025/04/15(火) 16:20:44

 xyz様
 >VBAに限定されていますが、ユースケースによるでしょう。
 >どのような場面で正規表現を必要としているのでしょうか
 申し訳ございません。特にこれといった具体的ケースはないのですが
 ゴリゴリ書いているときにふと「これ正規表現つかったら楽じゃね?」と思って質問させていただいた次第です
 ※ゴリゴリ書いていたのは私自身のタスクではなかったのでもう完了してしまいました

 ワークシートに記入しておいてEvaluateで取得する手もなかなかアリですね。なかった発想です。

(TKG) 2025/04/15(火) 16:45:33


 jindon様
 >VBAでの正規表現の一番のメリットはコードが短縮できることです。
 >が、あくまで私の経験からですが、処理速度は遅くなります。
 やってることからしてなんとなく遅そうな認識はありましたが。。
 ごり押しの方が(内部処理的には)はやいというジレンマですね。
 結局ごり押しも含め色んな手段を持っておきたいです
(TKG) 2025/04/15(火) 16:53:41

 (´・ω・`)様
 Python in Excelは早々に諦めましたね笑
 使い勝手が悪いというか、本来の使い方と違い過ぎて受け付けませんでした。
 UDFのような使い方がベストなのでしょうね
(TKG) 2025/04/15(火) 16:55:44

 (TKG)さんの  2025/04/15(火) 16:45:33発言拝見しました。
 さしあたって必要になっているわけではないと。了解です。

 ■私の発言の訂正です。
 |  ワークシートに関数を作っておいて、(B1:  =REGEXEXTRACT(A1,"\d+|\D+",1)  )
 |  ワークシートをユーザー定義関数のように見立てて利用することもできるでしょう。
 |      Dim v
 |      Sheet1.[A1] = "17uehdbi72637ug63hv"
 |      v = Evaluate("Sheet1!B1#")
 |      v = Application.Transpose(Application.Transpose(v)) '1次元配列
 (1)
 v = Evaluate("Sheet1!B1#")ではなく、
 単に  v = Range("Sheet1!B1#").Value とすれば2次元配列が返ります。
 (2)
 1次元配列を取りたければ、最初から
 v = Evaluate("TOROW(Sheet1!B1#)")
 とすればよかったですね。  Transposeを二度実行する必要はなかった。

(xyz) 2025/04/16(水) 17:59:05


 > 加えて図々しいお願いですが、「こんな使い方もあるよ」も教えていただければ幸いです。

 参考程度なのですが、2012年に作成したRTF(提示されたtextファイル)のタグを削除して文章のみを抽出するものです。

https://forum.ozgrid.com/forum/index.php?thread/113966-excel-vba-rtf-tags-removal-scrubbing-rtf-to-text-on-active-sheet/

 追加です

[[20160203175950]]

 CSVファイルの行データをSplit関数を使用して","で分割する際、"で囲まれた","では分割しないものです。
(jindon) 2025/04/17(木) 16:16:13

 質問の趣旨とは外れますが、
「IsNumericとMidで1文字ずつ判定してゴリ押し」のコードを書いてみました。

 Function mySplit(s$)
    Dim i As Long, pos As Long, res(), ub As Long
    pos = 1
    ReDim res(ub)
    For i = 1 To Len(s) - 1
       If IsNumeric(Mid(s, i, 1)) <> IsNumeric(Mid(s, i + 1, 1)) Then
            res(ub) = Mid(s, pos, i + 1 - pos)
            pos = i + 1
            ub = ub + 1: ReDim Preserve res(ub)
       End If
    Next
    res(ub) = Mid(s, pos)
    mySplit = res
 End Function

 Public Sub test()
    Debug.Print Join(mySplit("17uehdbi72637ug63hv"))
 End Sub

 冗長かどうかは人それぞれだと思いますので、ご参考までに。
(hatena) 2025/04/18(金) 07:20:34

コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.