人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

【正規表現】先読み,否定の先読み,についての質問です.
/usr/home/bin/hoge.txt
という文字列に対して,最後のスラッシュより後の文字列(hoge.txt)を取り出したいと思います.
まず私が考えた正規表現では
(?!.+/).+
というものですが,これだと,「/hoge.txt」にマッチしてしまいます.
+を*に変え,
(?!.*/).+
とすると,正しく「hoge.txt」にマッチします.
私の解釈では
(?!.+/)
は,「スラッシュで終わる,1文字以上の文字列のマッチが失敗する位置」という意味で,
(?!.*/)
は,「スラッシュで終わる,0文字以上の文字列のマッチが失敗する位置」という意味で,
この場合,どちらも同じ位置となるはずなのですが,なぜか正規表現の挙動は異なります.
これはなぜでしょうか? また,私の解釈はどこが間違っているのでしょうか?

●質問者: サロン日焼け
●カテゴリ:コンピュータ ウェブ制作
○ 状態 :終了
└ 回答数 : 3/3件

▽最新の回答へ

1 ● ヤマヤタケシ

この正規表現でいかがでしょうか?
/\/([^\/]+)$/


perlのテストコードです。(regrex_test.pl)

#!/usr/bin/env perl

$src="/usr/home/bin/hoge.txt";
$src =~ /\/([^\/]+)$/;
print( $1 . "\n" );


実行結果
./regrex_test.pl
hoge.txt


質問者から

すみません,私が聞きたいのは「正しい解答」や「動くコード」ではなく,
否定の先読みにおける正規表現が,「どういう意味で,なぜ,そのような挙動を示すのか?」という意味と理由の部分です.


2 ● a-kuma3

参考になるのは、こちら。

"(?=hoge)" は "hoge" が続くゼロ文字列に一致する。

http://d.hatena.ne.jp/ku__ra__ge/20070324/p2

ダイアリーに記載されている京大のページはリンクが切れていますが、Internet Archive に残ってます。
http://web.archive.org/web/20030427151113/http://www.cog.ist.i.kyoto-u.ac.jp/~takahashi/notes/regex_forward_match.html

id:ku__ra__ge さんと同じく、ぼくもこれを読んで、「う?む」って唸りました。


んで、質問されていることの説明です。

(?!.+/)

この部分を、どう読むかというと、「『任意の一文字以上の文字列で、/ でおわるもの』ではない文字列の先頭(文字の隙間)」です。
hoge.txt の前にある "/" (スラッシュ一文字) は、「『任意の一文字以上の文字列で、/ でおわるもの』ではない文字列」に該当しますから、先の表現は "/" の前を指します。
そこから任意の文字列ですから、"/hoge.txt" がマッチするわけです。

対して、

(?!.*/)

さきと同様に、「『任意のゼロ文字以上の文字列で、/ でおわるもの』ではない文字列の先頭(文字の隙間)」です。
「『任意のゼロ文字以上の文字列で、/ でおわるもの』ではない文字列」に該当するのは、"hoge.txt" になります。
後は、説明は要らないですよね。

これを踏まえると、以下の正規表現も "hoge.txt" にマッチするのが理解できると思います。

(?<=.+/).+

「幅ゼロの肯定後読み」を使ってます。
「"/" で終わる文字列の末尾(文字の隙間)」までが、肯定後読み (?<=χ) の部分ですから...


なんて、偉そうに書いてますが、先読み/後読みって、難しいです。
先読み/後読みの正規表現は、ひとしきり考え込まないと理解できません X-)


蛇足ですが、元の問題の一番わかりやすい正規表現は、これだと思います。

[^/]+$

3 ● ku__ra__ge
ベストアンサー

(?!.+/)
は,「スラッシュで終わる,1文字以上の文字列のマッチが失敗する位置」という意味
(?!.*/)
は,「スラッシュで終わる,0文字以上の文字列のマッチが失敗する位置」という意味

認識はこれで合っています。
ただ「(?!.+/).+」という正規表現は、否定先読みが『後』の位置に書かれていることが混乱の元ではないかと思います。
(一般的な横書きテキストの場合、正規表現の「先」は右側であり、「後」は左側です)

「(?!.+/)」は、「その位置から右側へマッチを試みた場合、スラッシュで終わる,1文字以上の文字列のマッチが失敗する位置」を示すため、それぞれの位置でのマッチ成否は以下のようになります。

【位置A】
/usr/home/bin/★hoge.txt
 ↑
 (「.+/」のマッチは失敗する)

【位置B】
/usr/home/bin★/hoge.txt
 ↑
 (「.+/」のマッチは失敗する ※)

※正規表現「.+/」は2文字目以降に「/」が現れないとマッチしないが、
 「/hoge.txt」は1文字目にしか「/」が無いためマッチしない。

【位置C】
/usr/home/bi★n/hoge.txt
 ↑
 (「.+/」のマッチが成功する)

正規表現はマッチする文字列が最長になるようにマッチしますので「(?!.+/).+」は右端の「.+」が最長になるように【位置B】が利用され、結果「/hoge.txt」がマッチすることになります。


質問者から

ありがとうございました.やっと腑に落ちる感じがしました.
これっぽっちのことで,丸一日考え込んでしまったので.やはり質問してみるものですね.


関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ