なでしこを明後日の方向に

日本語プログラム言語なでしこを応援しています

なでしこ3の正規表現マッチがなでしこ1と変わっている件

※ これが書かれたのは なでしこなでしこv3.3.83~v3.4.1あたりの頃です


私は正規表現に詳しくないので、なでしこ1の時もなんとなくで使っていました。
なでしこ3でもなんとなくで使ってみたら……なにかが違う!

たとえば、なでしこ1のサンプルにあるこのコード

「02:50:30」「(\d+):(\d+):(\d+)」正規表現マッチ
抽出文字列表示

\dが数値、+が後ろにつくとその連続、で\d+が数値の連続した部分です。
( )の中身が変数 抽出文字列に配列形式で返ってきます。

このプログラム、なでしこ3で実行すると抽出文字列に入らないんです!

続いて、このプログラム

「02:50:30」「\d+」正規表現マッチ
それ表示

なでしこ1では"02"が表示されます。
なでしこ3で"02,50,30"が表示されます。
["20","50","30"]の配列が返ってきているようです。

正規表現マッチはなでしこ1となでしこ3で非互換と言えるでしょう。

というわけで、なでしこ3の正規表現マッチがどう変わったか調べてみました。

変わっていない点

命令の引数は変わっていません。

・なでしこ1
 正規表現マッチ(AをBで|AがBに)
・なでしこ3
 正規表現マッチ(AをBで|AがBに)

変数 抽出文字列もなでしこ1、なでしこ3どちらでも変数定義されていました。

変更点「正規表現修飾子の指定方法」

正規表現には修飾子というものがあって、挙動を変えるスイッチのようなものがあります。

iを指定すると大文字と小文字を同一視する
gを指定すると複数マッチングするかを調べる

…などがあるようです。

この正規表現修飾子を指定する方法がすこし変わりました。

・なでしこ1の正規表現修飾子の指定方法

マニュアルより

修飾子を使ったマッチを行いたい場合は、Perlの形式でパターンを『m/パターン/修飾子』のように指定します。日本語(S_JIS)の文字列をマッチする場合は、オプションに「k」を追加します。

サンプルはこうなっています。

「マーチ」を「m/ー/k」で正規表現マッチ

m/で始まって、後ろの/kの部分が修飾子です。
修飾子は /gmkなどの感じで指定するそうです。

・なでしこ3の正規表現修飾子の指定方法

マニュアルより

パターンBは「/pat/opt」の形式で指定。optにgの指定がなければ部分マッチが『抽出文字列』に入る

先頭のmがいらなくなって、後ろの/optの部分を /gm とかなんかにするみたいです。

「gの指定がなければ部分マッチが『抽出文字列』に入る」とあります。
裏を返すと
「gの指定があれば『抽出文字列』に入らない」……ということ、か?

だから最初のサンプルでは抽出文字列が空だったんですかね。
もう一度最初の画像を見てみましょう。

「g」の指定は……ないです。 ないけど抽出文字列は空です。はて?

変更点「正規表現修飾子を省略した場合の挙動」

自分が正規表現マッチの命令を使うとき、修飾子を指定する形式『m/パターン/修飾子』(なでしこ1)『/pat/opt』(なでしこ3)の形で使っていませんでした。

なでしこには正規表現修飾子を省略した場合の挙動が備わっているので、それに乗っかっていたということになります。

なでしこ1で正規表現修飾子を省略した場合

正規表現修飾子という変数があり、初期値は文字列gmkです。
"g"はグローバルマッチ
"m"は複数行マッチ
"k"はSJIS対応
SJISの文章を改行を含めてまるごと見るよ」って感じでしょうか(自信無し)。

なでしこ1で正規表現修飾子を省略した場合、この変数 正規表現修飾子 が使われるようです。

なでしこ3で正規表現修飾子を省略した場合

マニュアルには書いていないので、ソースを確認しました

異国の言語で書かれていますね…… 雰囲気的に、パターンがない場合'g'を指定するって書いてあるように感じます。

ということは……省略した場合、「g」を指定したのと同じ動作をする……そして「gの指定があれば『抽出文字列』に入らない」……となります。

修飾子を省略した形とそうでない形を覚える

/パターン/修飾子』のように『/』がある書き方が修飾子を省略しない形で
パターンの前後に『/』がなければ省略した形です。

具体的には

「02:50:30」「(\d+):(\d+):(\d+)」正規表現マッチ //←省略した形
「02:50:30」「m/(\d+):(\d+):(\d+)/gmk」正規表現マッチ //←なでしこ1の省略しない形
「02:50:30」「/(\d+):(\d+):(\d+)/g」正規表現マッチ //←なでしこ3の省略しない形

そして次の形

//なでしこ3の省略しない形
「02:50:30」「/(\d+):(\d+):(\d+)/」正規表現マッチ 

これは「修飾子を省略せずに空を指定した形」です。
なでしこ3のサンプルにあるのはこの形です。

挙動のまとめ


「02:50:30」を「(\d+):(\d+):(\d+)」で正規表現マッチ

バージョン 修飾子 それ 抽出文字列
なでしこ1 省略形 文字列"02:50:30" 配列["02","50","30"]
なでしこ3 省略形 配列["02:50:30"] 配列[ ]

「02:50:30」を「(\d+)」で正規表現マッチ

バージョン 修飾子 それ 抽出文字列
なでしこ1 省略形 文字列"02" 文字列"02"
なでしこ3 省略形 配列["02","50","30"] 配列[ ]

「02:50:30」を「/(\d+):(\d+):(\d+)/」で正規表現マッチ

バージョン 修飾子 それ 抽出文字列
なでしこ3 空を指定 文字列"02:50:30" 配列["02","50","30"]

なでしこ1で省略した形と、なでしこ3で省略しない形がそっくりなので、プログラムを移植する場合はそのように。

結論

・命令は正規表現マッチ(AをBで|AがBに)で、なでしこ1と3で引数は変わらないが挙動に差がある

・引数Bには、正規表現修飾子を省略した書き方と、指定した書き方がある
指定した書き方はパターンの前後に『/』がある
「02:50:30」を「(\d+):(\d+):(\d+)」で正規表現マッチ というプログラムは正規表現修飾子を省略した書き方である
「02:50:30」を「/(\d+):(\d+):(\d+)/」で正規表現マッチ というプログラムは正規表現修飾子を指定した書き方である

・修飾子を省略した書き方をすると、なでしこ1では修飾子「gmk」が使用される
・修飾子を省略した書き方をすると、なでしこ3では修飾子「g」が使用される
・なでしこ3で修飾子「g」を使うと、抽出文字列が空配列になる
 ・以上よりなでしこ3で省略した書き方をすると抽出文字列が空配列になる

・なでしこ1で正規表現修飾子を省略すると、それに文字列で値が入る
・なでしこ1で正規表現修飾子を省略すると、抽出文字列に値が入る
・なでしこ3で正規表現修飾子を空に指定すると、それに文字列で値が入る
・なでしこ3で正規表現修飾子を空に指定すると、抽出文字列に値に値が入る

(なでしこ3で正規表現修飾子を省略すると、それに配列で値が入る)
(なでしこ3で正規表現修飾子を省略すると、抽出文字列が空になる)


なでしこ1での

  「02:50:30」を「(\d+):(\d+):(\d+)」で正規表現マッチ

というプログラムは「それ」「抽出文字列」に値が入ることを期待している

なでしこ3で同等の結果を期待する場合は 引数Bの部分を/ /で囲む書き方にすると良い。

  「02:50:30」を「/(\d+):(\d+):(\d+)/」で正規表現マッチ

そうすれば「それ」「抽出文字列」に値が入る。

※ これが書かれたのは なでしこv3.3.83~v3.4.1あたりの頃です


記事を書いた人はワイルドカードマッチ命令(なでしこ1)を応援しています