正規表現 “.*” と “.*?” と “[^”]*” の違いについて。 .*?が最短のマッチは間違い。

正規表現で、カッコの中身を表すときに、色々な書き方があります。HTMLタグにマッチさせたい時などに使えますね。

正規表現初心者はまず “.*” と書きたくなりますが、それでは思う通りにマッチしません。

 

正規表現 .*? の意味と使い方

インプット例

“aaa” bbb “ccc”

①この ”aaa” の部分にマッチさせようと思ったら、 ”.*” と書いてしまうと

“aaa” bbb “ccc”

と全てにマッチしてしまいます。これは

.*

aaa” bbb “ccc

このように最初のカッコと最後のカッコを選んでマッチさせてしまうからです。

なぜなら、正規表現ではデフォルトではなるべく長い部分を選んでマッチさせるため。

 

②続いて ”.*?” と書くと

“aaa” bbb “ccc”

と意図した通りにマッチしました。これは ? をつけることで最短のマッチをするため、とよく色んなサイトで書かれていますが、違います。

説明するので先の項目を見てください。

 

③続いて ”[^”]*” と書きます。

“aaa” bbb “ccc”

[^”] というのは、否定の表現で、^ のあとにきた記号以外の全て、という意味になります。

つまり、この場合だと ” 以外全て、そして最後に ” で閉められているので①の例だとかっこの中身に ” を含むため、マッチせず、②と同じ結果になります。

 

それでは次に、②と③の違いを見てみましょう。今の表現だと結果が同じですが、先ほど書いた正規表現に文末を表す $ をつけてみてください。文末を含みカッコとその中身ということは、

“ccc” が選択されそうですが、どうでしょうか。

 

④ ”.*?”$ こうすると

“aaa” bbb “ccc”

今度は全てが選択されました。もし .*? の表現が最短のマッチになるのなら、 “ccc” となるはずですが、なりませんでした。

これは .*? の表現が最短ではなく、「左端の一文字からテストして、最初にマッチした部分」になるからです。今回の場合文末を指定する$が入っているため、左端から始めて、最初にマッチするのは、最初の”と最後の”を含めた場合になります。

英語ではこのように最初のマッチで終わらせてしまうので、Lazy = 面倒くさがりの表現と言われています。逆に、 .* のようになるべく長く取ろうとする表現を Greedy = 貪欲の表現と言います。 Lazy や Greedy は正規表現に限らず、プログラミングの中で、一般に使える表現です。

では”ccc”を選択したければどうすれば良いのでしょうか? 今回は中身のcccなどは何にでもなれるものとしてマッチさせるので、カッコの中身の指定はしません。

 

⑤最後に ”[^”]*”$ を試してみましょう。

“aaa” bbb “ccc”

末尾に$を含めます。するとこのように意図した通り、最後のカッコが、最短の形でマッチしました。上記の④のパターンではマッチしたカッコの中身にaaaの終わりの ” と ccc の初めの ” が含まれているためマッチが起きず、中にカッコがない、かつ文末の$が含まれる”ccc”がマッチしました。

結果を比べてみると、 “[^”]*” が一番意図した通りの結果を出しやすく、またパフォーマンスにおいても優れています。

正規表現では、よく多くを書き具体的に示した方が早く正確なマッチを行うことができます。 .*? は書く上でもシンプルで、まただいたいの場合では最短のマッチとなるので使いやすいですが、例外があるので気をつけないといけません。

.*? を使う上でポイントなのは、一番左端からスタートするということです。

なので、普通の表現の場合は最短の表現という誤解をされやすいのです。しかし実際は、 .*? は左端から始めて、最初にマッチするものの表現です。なのでこれを理解していないと、正規表現の末尾、右端の表現によって意図したマッチが起きない場合があります。

今回の例の$のように特殊な位置表現を含むと結果が変わってしまう可能性があるということです。

慣れるまではあまり感覚的でないのでちょっと難しいですが、この .*? の表現は最初のマッチが起きればそこで処理が終わるため、マッチの回数では節約できる場合もあり、左からテストして最初のマッチ、Lazyであるということを意識して書くと効率の良い正規表現が書けます。

 

正規表現のデバッグサイト

こちらに、参考になるサイトを貼っておきます。

https://regex101.com/

この Regex 101 というサイトでは、正規表現と、マッチさせるテキストでどのように処理されるかテストできます。

また入力した正規表現が、具体的にどのような手順を追ってそのマッチにたどり着いたかを見ることができます。正規表現を試したあと、左サイドのDebugというところを押すと表示されます。また結果までの到達時間やステップ数も表示できるので便利です。

具体的にどのようなステップで処理が行われているのか分かるので、正規表現の詳細を理解しやすいですし、それを踏まえて無駄のステップを避け、より高速な処理を目指すことができます。このようなデバッグツールは大事ですね。

 

以上正規表現で紛らわしい3つの表現の違いでした。 .*? の意味が分かりましたでしょうか?

今度はまた正規表現のパフォーマンスについても書きたいと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です