* U K I Y A H O N P O *
Nel mezzo del cammin di nostra vita mi ritrovai per una selva oscura,
che la diritta via era smarrita.
リロード   新規 下位ページ作成 編集 凍結 差分 添付 コピー 名前変更   ホーム 一覧 検索 最終更新 バックアップ リンク元   ヘルプ   最終更新のRSS
浮子屋商店もよろしく。

正規表現講座/11 のバックアップの現在との差分(No.2)


  • 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:間違いだらけの正規表現講座 level 11

#contents
&color(Red){現在このページは書き直し中です。};

* 再開 [#b0969a7d]

さて、一旦クローズしたこの講座ですが、正規表現には他にも色々な記号があります。~
それらは使う機会は少なかったりしますが、「こんなことも出来る」と知っておけば、~
必要になった時には劇的な効果を発揮する表現だったりします。

ただし、これ以降で紹介する正規表現は、今までよりさらに「処理系に依存」する、~
つまり、エディタやソフトによって機能が違ったり使えなかったりする場合がある~
表現だと思ってください。

原則としてこれ以降の講座では Microsoft .NET の正規表現ライブラリをベースに、~
Perl5系やPCREライブラリの正規表現あたりを眺めつつ、使えそうな表現について記述していこうと思います。~
.NETとPerl系とで表現の仕方が違うような場合は適宜注釈を入れることにしますが、~
その他の処理系(「古い」エンジンを使ったエディタ、vi、JavaScript、シェルなど)は~
おいてけぼりになる可能性があります。

* 先読み、前読み [#j790798b]

今回は、「先読み」「前読み(戻り読み)」について記載します。

これ、第10回までに書こうかどうか悩んだのですが・・・~
説明がややこしいのと、使う頻度が高くないこと、~
実装していない処理系がちらほらあることから割愛しました。

でも最近、やっと日本語で一言で説明する方法を思いつきました。~
「先読み」「前読み」は、要するに日本語で一言で言えば「但し」です。~
具体例を見てみましょう。

* 数値を抜きたいけど・・・ [#x6b803fb]

文章の中から、数値を抜き出したいとします。~
ここまで読んだ方ならすぐに思いつきますね。

 \d+

これで数値にマッチします。


ですが、「100円」というように、後に「円」が来る数値は抜き出したくないとします。~
どうしますか。

 \d+[^円]

思いつくのはこうではないでしょうか。~
数字が来て、その後に「円」以外の文字が来る、と。よしよし。と思って試してみると、~
困ったことに気づくと思います。

つまり、「ゆねねは100けのっぴ」というような文章があった場合、先の例では、

 100け

がマッチしてしまうのですね。欲しいのは「100」の部分だけだというのに。

* ただし! [#qf84dcdb]

こんなときに登場するのが、「先読み」です。~
実際に書いてみましょう。

 \d+(?!円)

これで~
「数値の1文字以上の繰り返しにマッチする。&color(Red){ただし、後に『円』が来ないこと。};」~
という意味になります。~
&color(Red){このとき、「ただし」の部分はマッチの対象にはなりません。あくまでマッチするのは「\d+」の部分だけです。};

この「ただし」のバリエーションとしては以下のものがあります。

 (?=)
 (?!)
 (?<=)
 (?<!)

|種類|正規表現|意味|h
|先読み|(?=任意の正規表現)|ただし、後に「任意の正規表現」が続くこと|
|否定先読み|(?!任意の正規表現)|ただし、後に「任意の正規表現」が続かないこと|
|前読み|(?<=任意の正規表現)|ただし、前に「任意の正規表現」が来ること|
|否定前読み|(?<!任意の正規表現)|ただし、前に「任意の正規表現」が来ないこと|

なお、これらの正規表現に使うカッコ「()」は、グループ化やキャプチャのためのカッコとはみなされません。~

* グループ化でいいんじゃないの? [#scfd922d]

さて、さっきの「円が続かない数値」を抜き出したいのであれば、何もそんなことをしなくても、~

 (\d+)[^円]

のようにグループ化しておいて、「$1」で取り出せばいいんじゃないの、と思われた方、するどい。~
それで足りてしまう場合も多いのですが、以下のような場合に困ります。

** 困る場合1 [#eefcf40a]

前後に文字が続かないケースがある場合。~
「(\d+)[^円]」では、「100」とだけ書かれた文章にはマッチできません。
「\d+」がマッチする「100」の後に何も文字が来ないから、「[^円]」の部分がマッチできないのですね。

** 困る場合2 [#abc1d636]

余分なものをマッチすると他のマッチが殺される場合。~

「100けののけ」という文章から、「後に円が来ない数値」と「けののけ」を抜き出したい場合・・・~
もうお分かりですね。

 (\d+)[^円]|けののけ

のように書いても、最初に「100け」をマッチさせてしまうと、残るのは「ののけ」。「けののけ」はマッチできません。

* 注意点 [#xca7fab0]

さっきのバリエーションの表では「任意の正規表現」と書きましたが、これらの「先読み」「前読み」の中では、~
「繰り返し」を表す「*」や「+」は使えなかったり、あるいは動作が制限される((バックトラックしない等))
処理系が多いです。

* ちょっと脱線 [#v2c38911]

今回、日本語で「ただし!」と書きましたが、正確にはこれらの「先読み」「前読み」は、ゼロ幅アサーション、~
つまり、「^」や「$」と同じように、「位置を表す正規表現」の一種です。

「^\d+」と書けば、「行の先頭位置」「数字の1文字以上の繰り返し」という意味になりますが、同じように、~
「\d+(?!円)」で、「数字の」「1文字以上の繰り返し」「その先に円が来ない位置」を表しているわけです。

「位置を表す正規表現」であるがゆえに、文字にはマッチせず、余分な文字を食べてしまうことがないわけですね。

* ご意見などはこちらへ [#v31ce52c]
- \d+(?!円)では「100円」の「10」にマッチすのでは -- hogehoge &new{2010-09-22 (水) 17:00:20};
- ご指摘の通りですね。もっと良い説明の仕方を思いつくまでこのページは封印しておきます。 -- うきや &new{2010-09-25 (土) 20:04:57};
- 「([0-9]*)けののけ」ではダメでしょうか? -- hogehoge &new{2010-11-10 (水) 08:52:58};

#comment