最近エクセルの神髄(@yamaoka_ss)さんが出題されている、#VBA100本ノック を時間を見て解答している。
今回以下の問題にチャレンジして自分なりの解答を出した後で模範解答を見たところ、非常に学びの多いものだったので備忘録。
#VBA100本ノック 16本目
— エクセルの神髄 (@yamaoka_ss) 2020年11月4日
セル内改行はAlt+Enterですね。
引数の文字列から無駄な改行(LF)を削除して返すFunctionを作成してください。
※CRLFはLFに変換する。
■無駄な改行とは
・文字列の前後の改行
・2連続の改行
サンプル:改行(\n)
\n無駄な\n\n改行を\n\n\n削除\n\n
↓
無駄な\n改行を\n削除 pic.twitter.com/jm0uIs46EJ
自分なりの解答
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を使用する方法)特有のものかとツイートしたところ神髄さんからコメントを頂きました。
そんなことないですよ。
— エクセルの神髄 (@yamaoka_ss) 2020年11月23日
参照設定して、
With New RegExp
これで出来ます。
試してみたところ確かにレイトバインディングの時と同じ様に変数の宣言・代入を省略することが出来ました。
改行されない?
お題通りに改行が文中に存在するはずなのに何故か改行して表示されず、ボヤいたところVBAerやまと(@yamato_1413)さんから下記のアドバイスを頂きました。
セルの書式を折り返して全体を表示にしないとだめなんですよそれ
— VBAerやまと(にわかHaskeller) (@yamato_1413) 2020年11月23日
設定をしたところ無事に表示されました。
普段誰かにコードを見てもらうという状態になることがなく、この#VBA100本ノックにチャレンジするということはとても勉強になります。
- つたないコードをジャッジされる。
- 与えられた課題を解決しようとする。
- それを継続していく。