- ベストアンサー
VBAの正規表現で最短の括弧内を取得する方法は?
- VBAの正規表現で最短の括弧内を取得する方法について教えてください。
- 指定した正規表現パターンで実行すると、最短ではなく長い括弧内がマッチしてしまいます。
- 最短の括弧内を取得するには、どのようにすれば良いのでしょうか?
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
こんばんは。 こんな感じかな? >"[(|(].*?[)|)]$" VBScript の正規表現には、.*? の「最短マッチ」がなかったと思います。あれば、私は苦労しなかったと思います。それで、タイプライブラリを入れて、BRegExp なんていうものを代用したりするわけですが、他人の環境では強要できません。そうすると、他は、やはり、InstrRev などを使って取り出して使うことになってしまいます。 今回は、仕方がないので、中に入れる文字を特定して、マッチする部分を区分けしなければいけません。 \w+ の場合は、数字、アルファベット、アンダーバーに限ります。 [ぁ-龠]+ なら、全角文字です。 それと、私が書くパターンは、最後尾の$を用いずに、(a) を数えて、その最後を出すという方法を取ります。 以下のコードの部分を変えます。 Pattern = "[\((]\w+[)\)]" 'パターン str2 = RegMc(RegMc.Count - 1) '抽出 '------------------------ Sub sample1() Dim RegMc As Variant Dim str1 As String Dim str2 As String str1 = "test(a)x(b)y(c)" str2 = "" With CreateObject("VBScript.RegExp") .Pattern ="[\((]\w+[)\)]$" 'パターン .Global = True Set RegMc = .Execute(str1) If RegMc.Count > 0 Then str2 = RegMc(0) '抽出 End If MsgBox (str1 & vbLf & str2) End With End Sub
その他の回答 (6)
- Wendy02
- ベストアンサー率57% (3570/6232)
こんにちは。 一応、自分の描いていたものをコード化してみました。 >句読点、「(1)」「々」などの使いそうな文字もはねられてしまうので、範囲を段々広げてみたりもしましたが カッコの中に、カッコがある場合に、除外するようにしてみました。 Const SRC As String = "(株),(財),(a)" '検索語 Const REP As String = "α,β,γ" '臨時の置換語 このように臨時に置き換えます。参考にしてみてください。もし、分からない部分がありましたら、新たに質問を出してください。 '---------------------------------------------------- Const SRC As String = "(株),(財),(a)" '検索語 Const REP As String = "α,β,γ" '臨時の置換語 'SRC と REP の数は、必ず揃えてください。 Sub Test1() Dim str1 As String Dim ret As String str1 = "test(a)x(b)y(c(a))" ret = rePickUp(str1) MsgBox str1 & vbCrLf & ret End Sub Function rePickUp(mTxt As String) Dim Matches As Object Dim rTxt As String Dim i As Integer mTxt = RepW(mTxt, False) With CreateObject("VBScript.RegExp") .Pattern = "\([^\)]+\)" .Global = True Set Matches = .Execute(mTxt) i = Matches.Count If i > 0 Then rTxt = Matches(i - 1) End If End With rTxt = RepW(rTxt, True) rePickUp = rTxt End Function Function RepW(mTxt As String, Optional flg As Boolean = False) Dim i As Integer Dim oTxt As String Dim ret As String Dim wdRep1 As Variant Dim wdRep2 As Variant wdRep1 = Split(SRC, ",") wdRep2 = Split(REP, ",") oTxt = Replace(mTxt, ")", ")", 1) oTxt = Replace(oTxt, "(", "(", 1) If flg = False Then For i = 0 To UBound(wdRep1) ret = Replace(oTxt, wdRep1(i), wdRep2(i)) Next i Else For i = 0 To UBound(wdRep1) ret = Replace(oTxt, wdRep2(i), wdRep1(i)) Next i End If RepW = ret End Function
お礼
締め切りが早すぎたようで、申し訳ありません。 一時置換ですね。 時々、同様の方法を使わせていただいています。 私の場合は、入力できない文字(ESCなど+連番とか)を利用することが多いですが… 今回は、入力文そのものは入力者の整理方法にまかせている部分が多いので、どのような2重括弧があるのか(ないのか)想定するのが難しいのと、似たような(似ていて違う)マッチングを他にも何種類かやっているので、一つの処理はできるだけ簡単に済ませてしまいたいという気持ちもありました。 全体としてはリトライ可能な処理なので、使用者が「えっ!なんで?」と思うような誤解釈を生じない範囲であれば、完全性はそれほど追求しなくても良いという部分もありました。(より良いに越したことはありませんが) わざわざ、追加の回答をいただきありがとうございました。
- Wendy02
- ベストアンサー率57% (3570/6232)
こんにちは。 >全角英数や句読点、「(1)」「々」などの使いそうな文字もはねられてしまうので、 #4のレスを読みましたが、本格的に作るとなると、作り直さないといけないように思います。VBAというよりは、正規表現や文字処理も問題です。実際の文字列を見て、パターン自体を考え直さないといけないような気がします。あまり単純な方法ではできないかもしれません。もともと、VBA内は、Unicode ですから、JISのように一括でできるわけではなく、リストが分散しています。 例:"[\u2421-\u2473\u2521-\u2576\uFF66-\uFF9F0-9A-z]+
お礼
>VBA内は、Unicode ですから、JISのように一括でできるわけではなく 今まであまり意識してはいませんでしたが、[ぁ-龠]+から範囲を拡張しようとして、どうやらUnicodeらしいことは確認していました。 >例:"[\u2421-\u2473\u2521-\u2576\uFF66-\uFF9F0-9A-z]+ これに似たようなことをやってたのですが、いっそのこと「()以外で」という考えに至ったわけです。(最短の意味では「)以外」が正しいのかも) いろいろお付き合いいただき、大変ありがとうございました。
- Wendy02
- ベストアンサー率57% (3570/6232)
こんにちは。 もともと最短マッチは、一番先に見つけていたものに対して、最短距離のものを引き出すわけですから、意味が違いますね。今回、(a) (b) (c) の数が決まらないとすれば、しばらく考えてみましたが、以下の方法を考えてみました。ここの掲示板の常連さんたちが考えるパターンのような気がします。(笑) 全角、半角の問題が少し見にくくなりますから、Replace を使ったらどうでしょうか。(これは私が良く使うパターン)いずれにしても、実務的には、最終的には、統一するのではないでしょうか? "[\((][^\((]+[)\)]" ↓ "\([^\)]+\)" Sub sample2() Dim RegMc As Variant Dim i As Integer Dim str1 As String Dim str2 As String str1 = "test(a)x(b)y(ccccaa)" '←全角が入っています str1 = Replace(str1, "(", "(", , , 1) str1 = Replace(str1, ")", ")", , , 1) str2 = "" With CreateObject("VBScript.RegExp") .Pattern = "\([^\)]+\)" .Global = True Set RegMc = .Execute(str1) i = RegMc.Count If i > 0 Then str2 = RegMc(i - 1) End If MsgBox (str1 & vbCrLf & str2) End With End Sub 正規表現を使わない方法 (私が実際に書く方法です。RegExp は、オブジェクトを最初に作っておくか、参照設定するなら良いのですが、インスタンスが生成されますから、もし、短い期間なら、VB関数で処理してしまいます。ネット検索では、こちらの方が速いようです。複雑なものには向いていません。) Sub sample3() Dim RegMc As Variant Dim i As Integer Dim j As Integer Dim str1 As String Dim str2 As String str1 = "test(a)x(b)y(ccccaa)" '←全角が入っています str2 = "" i = InStrRev(str1, "(", , 1) '1...TextCompare j = InStr(i, str1, ")", 1) If i > 0 And j > 0 Then str2 = Mid$(str1, i, j - i + 1) End If MsgBox str1 & vbCrLf & str2 End Sub 私は、この種のマクロが一番多いのに、少しも覚えないですね。(^^;Perlの勉強を辞めてしまったからですが。
お礼
>半角の問題が少し見にくくなりますから、Replace を使ったらどうでしょうか。 これは処理が簡単になることもあり、頭の片隅にはありましたが、入力者のミスの場合はOKですが、意図的に使い分けている可能性も考慮すると、使わないですむならそれに越したことはないと考えていました。 >実務的には、最終的には、統一するのではないでしょうか? ん~、そうですね。 「行末に(…)がある場合は、○○の意味とする」などのルールも同時に決めているので、ルールの方で半角( )のみに限定してしまうとかするほうがいいのかなと考えているところです。 (あくまでも、入力文は尊重してあげようと…) >"\([^\)]+\)" 「最短」の意味からは、[ ]内は)のみでOKなのですが、$を付けた場合、パターンの先頭からが優先されるのか、後ろからが優先されるのかよくわからなかったので… ( 実験してみろって? すんません。) まぁ、当初は考慮していなかった2重括弧の場合なども考えてしまったりしたこともあるのですが。(当初の質問の最短とはズレてきてますが…) >正規表現を使わない方法 なるほど。 プリミティブですが正規表現などで悩まずにすみますね。(笑) 今回は同じ文章に対して、何種類かのマッチングでテストして、文章の構成と解釈を決めていることもあったので、はなからパターンだけで処理できる正規表現に走っていました。 速度も速いとのことですので、覚えておきます。 いろいろ、追加情報をありがとうございました。
- Wendy02
- ベストアンサー率57% (3570/6232)
#3 の補足の部分 >関係のないつもりでいた、"[(|(]"部分も訂正いただき恐縮です。 \( も ( も両方とも行けるようでしたが、なんとなく、違っていたような気がしました。 | は、その左右が、二文字以上で、文字列を( ) で括った場合に、その文字列どちらかになる、ということだったと思います。この場合は、どちらでも同じです。 VBScript の正規表現は、簡単なようでも、逆に、標準ではありません。Perl 標準のものを出してほしいものですね。 他は、また、見てみます。
お礼
補足への解説まで、わざわざありがとうございます。 >| は、その左右が、二文字以上で、文字列を( ) で括った場合に、 >その文字列どちらかになる うっかり混同して、[ ]の中で使ってしまってました。 そこまで見通して訂正していただいていたので、恐縮した次第です。 対象が自由入力文字列なので、入力可能な文字は極力そのまま通したいというのがやっかいなところです。(事前に「(」→「(」や半角英数などへの変換をしておけば、少しは簡単になるのですが…) おまけに、似たようなマッチングを数種類行っているので。 さて、教えて頂いた[ぁ-龠]+だと全角英数や句読点、「(1)」「々」などの使いそうな文字もはねられてしまうので、範囲を段々広げてみたりもしましたが、最終的に、()以外の文字の繰り返しを許すということで、 "[\((][^\(\)()]+[)\)]$" というパターンでどうやらうまくいきそうです。(最後尾判定もできているみたい) これでも、()内に(株)みたいなのが入っていると、はねてしまうのですが、まぁしかたがないかと。(これ以上は、私には荷が重いので) 一時は、"[)\)]$"にマッチした場合で、"[\((].*?[)\)]"にマッチする最終のものという2段階方式でもしかたないかと思っていたのですが、なんとかなりそうです。 いろいろとお知恵を、ありがとうございました。
補足
お礼を書いちゃったので、補足に追加ですが、 おまじない程度に2重括弧までを考慮して、こんなところかなと考えています。 "[\((](([\((][^\(\)()]+[)\)])|[^\(\)()])+[)\)]$"
- n-jun
- ベストアンサー率33% (959/2873)
#1です。 もしかして str1 = "test(a)x(b)y(c)abcd" でも最後尾の(c)を取得したいなら、 Sub sample() Dim RegE, RegMc Dim str1 As String, str2 As String Set RegE = CreateObject("VBScript.RegExp") str1 = "test(a)x(b)y(c)abcd" str2 = "" RegE.Pattern = "\(.?\)" RegE.Global = True Set RegMc = RegE.Execute(str1) MsgBox RegMc.Item(RegMc.Count - 1) End Sub こんな感じとか?
お礼
いろいろ気を回していただき、大変恐れ入ります。 今回は、このケースではありません。 >最後尾 No1の回答のケースでOKなんですが…
- n-jun
- ベストアンサー率33% (959/2873)
最短とは最後尾で良いのでしょうか? Sub sample() Dim RegE, RegMc Dim str1 As String, str2 As String Set RegE = CreateObject("VBScript.RegExp") str1 = "test(a)x(b)y(c)" str2 = "" RegE.Pattern = "\(.?\)$" MsgBox RegE.Execute(str1)(0) Stop Set RegMc = RegE.Execute(str1) If RegMc.Count > 0 Then str2 = RegMc(0).Value str1 = RegE.Replace(str1, "") End If MsgBox (str1 & vbLf & str2) End Sub こうゆう事とは違いますか?
お礼
回答ありがとうございます。 >最後尾で良いのでしょうか? はい。最後尾のつもりです。 確かに、「.?」だと「(c)」はマッチするのですが、()内に複数文字列があるので(例では1文字でしたが…)、「(cd)」がマッチしなくなるため、「.*?」としていました。 でも、No3様の情報を見ると、どうやらダメみたいですね…
お礼
研究した結果、なんとかなりそうです。 ありがとうございました。
補足
回答ありがとうございます。 関係のないつもりでいた、"[(|(]"部分も訂正いただき恐縮です。(やっぱり苦手) >VBScript の正規表現には、.*? の「最短マッチ」がなかったと思います 最初にそう思って、「最後尾」の$をとってテストして見たところ、「.*」では「(a)e(b)f(c)」が、「.*?」では「(a)」、「(b)」、「(c)」それぞれがマッチするので、わからなくなった次第です。(MS得意の仕様なのかなぁ?) >最後尾の$を用いずに、(a) を数えて、その最後を出すという方法を >取ります。 これだと、最後尾ということを別途判定する必要がでてきますね。 でも、いいヒントをいただきましたので、明日、研究してみます。