Excelが大好きだ!

Excel大好き経理マンがExcelの事を書き綴っていきます。


スポンサードリンク

正規表現で特定の文字の前にある文字をヒットする方法

最近エクセルの神髄(@yamaoka_ss)さんが出題されている、#VBA100本ノック を時間を見て解答している。

今回以下の問題にチャレンジして自分なりの解答を出した後で模範解答を見たところ、非常に学びの多いものだったので備忘録。

自分なりの解答

Function VBA100ノック016(無駄な改行あり文 As String) As Variant
     Dim myReg As RegExp: Set myReg = New RegExp
     Dim Ans As Variant
     
     myReg.Global = True
     Ans = 無駄な改行あり文
     
     myReg.Pattern = "\r\n"
     If myReg.test(Ans) Then
          myReg.Execute (Ans)
          Ans = myReg.Replace(Ans, vbLf)
     End If

     myReg.Pattern = "\n{2,}"
     If myReg.test(Ans) Then
          myReg.Execute (Ans)
          Ans = myReg.Replace(Ans, vbLf)
     End If
     
     myReg.Pattern = "^\n"
     If myReg.test(Ans) Then
          myReg.Execute Ans
          Ans = myReg.Replace(Ans, "")
     End If
     
     myReg.Pattern = "\n$"
     If myReg.test(Ans) Then
          myReg.Execute Ans
          Ans = myReg.Replace(Ans, "")
     End If
     VBA100ノック016 = Ans
End Function

これでも一応回答を導き出すことは出来ている。
これを(悪い意味で)覚えて頂いて模範解答を見て頂きたい。

エレガント模範解答

Function VBA100_16_02(ByVal arg As String) As String
  With CreateObject("VBScript.RegExp")
    .Global = True
    .Pattern = "^\n+|\n+$|\n+(?=\n)"
    VBA100_16_02 = .Replace(Replace(arg, vbCrLf, vbLf), "")
  End With
End Function

一応自分のコードと模範解答は同じ結果を表現しているわけですが、スキルによってこれほど記述が変わってくるという良い例です。


正規表現

正規表現とはなんぞやと言う話は割愛させて頂きますが、簡単に言うと

文字列の集合を一つの文字列で表現する方法の一つである 正規表現 - Wikipedia


私が使用した正規表現は下記です。

正規表現 内容
"\r\n" 改行(CRLF)
\n{2,} 改行(LF)が2文字以上
^\n 文章の頭にある改行(LF)
\n$ 文章の末にある改行(LF)

私はこの処理を正規表現1つずつ処理をしていましたが、神髄さんは「|」と関数のネストを利用することで1度に処理をされています。
正規表現「|」はいづれかの条件、ORのような役割しています。

上記以外に神髄さんが使用されている正規表現があります。

肯定先読み

正規表現 内容
\n+(?=\n) 改行の前にある改行

この正規表現で2つ並んでいる改行のうち、1つ目の正規表現だけを選択することが出来ます。
私が使用した「\n{2,}」は2つ以上並んでいる改行を全て選択することになってしまい、行頭・行末の処理と2つ以上の改行の処理が異なる(改行削除と改行を1つにする)ため、処理を1つにまとめることが出来ません。

CRLFをLFに変換する処理を正規表現ではなく、Replace関数を使用して正規表現の変換条件をLF削除のみにすることで正規表現処理を複数になることを防いでいます。

Withでオブジェクト変数省略

Withステートメントを利用してオブジェクト変数の宣言・代入を省略している処理もびっくりしました。

通常であれば私のように

Dim myReg As RegExp
Set myReg = New RegExp    

変数の宣言代入を行います。

一方、神髄さんは

With CreateObject("VBScript.RegExp")
...
end with

この様に記載することでwithステートメントの範囲内であれば変数の宣言なしにオブジェクトが存在している状態になっています。

私はこれをレイトバインディング(参照設定せずにCreateObjectを使用する方法)特有のものかとツイートしたところ神髄さんからコメントを頂きました。

試してみたところ確かにレイトバインディングの時と同じ様に変数の宣言・代入を省略することが出来ました。

改行されない?

お題通りに改行が文中に存在するはずなのに何故か改行して表示されず、ボヤいたところVBAerやまと(@yamato_1413)さんから下記のアドバイスを頂きました。

設定をしたところ無事に表示されました。


普段誰かにコードを見てもらうという状態になることがなく、この#VBA100本ノックにチャレンジするということはとても勉強になります。

  • つたないコードをジャッジされる。
  • 与えられた課題を解決しようとする。
  • それを継続していく。

いづれも自分のVBAスキルの向上につながると思い少しずつでも進めていきたいと思います。