ものぐさに複数マッチさせるPerlさん

文字列を数文字ずつの配列にするためにはどうすればいいだろう。
というのは、.NET Frameworkなら、StringクラスのToCharArrayメソッド使えという話だが、
Perlならどうするかというといろいろある。

@a=split //,$s;
@a=$s=~/./g;

splitのパターンを//にする方法が一般的らしい。
しかし、2つ目の正規表現を使った方法の方がわかりやすい気がする。
gを付けると複数マッチできて、結果がリストとしてもらえる。
()でグループ化されてたら、グループ化された分だけのリストになるけど。
あとあまり気にすることではないかも知れんけど、改行を含むなら改行文字はsを付けないとマッチしない。


しかし、/./gとは不気味な正規表現ですね。
なんでも1文字マッチするとだけ書いてあると。
gをつけるとこの条件に当てはまるもの全部抜き出してくれる。
例えば’tom’という文字列なら、まずtがマッチする。次に進んでoにマッチ、進んでmにマッチ。
結果、(‘t’,’o’,’m’)というリストが得られる。
この正規表現のマッチで、マッチしては前進を繰り返すというのはポイントかもしれん。


さて、この正規表現マッチというのは非常に便利なのですが、
まぁあまりに凶暴というのも確かな話ですね。
‘[100][200][300]’という文字列について、/\[(.+)\]/gでマッチさせてみた。
[と]に囲まれた部分だけを抜き出したかったのでしょう。(‘100′,’200′,’300’)というリストが出てくればいいですね。
しかし、これやると、結果は’100][200][300′ 1つだけになってしまう。
まぁなんでかというと、.というのは何にでもマッチするんだから、]だろうが[だろうがマッチすると。
そして、Perlの+とかいうのはもっとも長くなるマッチを選ぶ。
だから[100 と読み進めていって、]が来た時点で終わりと早合点せずに、じっくり読んでいって、
もっとも長い結果が出るようにすると。
当然ですが、'[100][200][300]’も'[100]’も'[200]’も'[300]’も'[100][200]’も'[200][300]’も
いずれも/\[(.+)\]/にはマッチします。
さて、しかし早合点してくれた方がありがたいので、ものぐさマッチというのが便利です。
+とか*とか{3}とか{1,20}とか{3,}とかいう量を指定するやつの後ろに?を付ければいい。
なので、/\[(.+?)\]/gでやると、結果は(‘100′,’200′,’300’)となる。


逆に最長マッチがありがたい話。
文字列の頭から10文字ずつ切っていった配列が作りたいと。
最後の要素は10文字未満でも仕方ないですけど。
そこで、/.{1,10}/g でマッチさせました。任意の文字1文字以上10文字以下ということ。
例えば’abcdefghijklmnopqrstuvwxyz’にマッチさせたら、
(‘abcdefghij’,’klmnopqrst’,’uvwxyz’)となる。最後の要素を除けばいずれも10文字ですね。
ところがこれを/.{1,10}?/gとすると、かなり困ったことになる。
(‘a’,’b’,’c’,’d’,’e’,’f’…)と1文字ずつ配列に入る。
これはまずaにマッチする、一応これでも完結できるので終わり。
とまぁ早合点にもほどがあると、そういう状態になるわけです。


まぁこういう都合があるので、正規表現はいろいろテストした方がいいですね。
この強力な性質で困ることもありますから。
そしてものぐさマッチは非常に便利ですから大いに活用しましょう。
もしかすると ものぐさマッチの方が複数マッチでは使うことが多いかも知れん。
複数マッチさせるのはテキスト中から、[foo]~[/foo]で囲まれた範囲を取り出すとか、
そういうことが多いけど、こういうのはものぐさマッチを使うとうまく取り出せそう。
まぁ他にはテキスト中から電話番号とか取り出すというのが考えられるけど、
こういうのは逆にものぐさマッチはだめですね。
06-4793-9111というのとかにマッチするなら/(?:\d+-)+\d+/gとかかな…
(?:~)は$1とかで取り出さないグループ化ですよ。
しかしこういうのは精度悪いし嫌いですけどね。特にURLとかメールアドレスとか。
便利に複数マッチを使いましょうということです。