『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ファイル)のタグを削除して文章のみを抽出するものです。
追加です
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.