タイトルにもあるとおり、ゼロ幅の(否定的)先読みです。
http://www.example.com/index/~~
http://www.example.com/list/~~
上記二つのディレクトリ以外を参照する
全ての URL についてマッチする正規表現が必要でした。
先頭が、index または list でなければいいのだから、
[^a-z] のような否定表現と、
(abc|xyz) のような論理和グループを用いて、
^[^(list/?|index/?)][\w/\.]+
とすればいいと思ってました。
ところが、それじゃ動かない…。
色々調べていると、以下のように書けば、出来るということが分かりました。
^(?!(list|index)(/|$))[\w/\.]+
これで、赤字の URL にはマッチしませんが、
青字の URL にはマッチするようになりました。
- index
- index/
- index/sample/file.php
- list
- list/
- list/directory/
- detail
- detail/
- detail/action.php
- index-hoge
- list-fuga/input.php
それでは、正規表現の解説をしましょう。
まず、1 文字目の ^ は先頭を表す記号です。
次に、(?!(list|index)(/|$)) の部分ですが、
これをさらに細かく分けて解説しましょう。
まず、(list|index) の部分は、
list または index という文字列に相当する、という意味になります。
そのあとに続く、 (/|$) の部分は、
/ または 行末 に相当する、という意味になります。
これら二つを合わせると、
- index
- index/
- list
- list/
上記の四つの文字列に相当する正規表現になります。
?! の部分の説明は少し後にします。
後ろについている、[\w/\.]+ はややこしくみえるかもしれませんが、
[A-Za-z0-9/\.]+ と同じ意味です。
英数字、スラッシュ、ピリオドだけで構成される 1 文字以上の文字列、ということになります。
ちなみに、ピリオドの前の \ は単なるエスケープです。
さて、要件として必要な駒がそろいました。
- (list|index)(/|$) に該当しない文字列
- [\w/\.]+ に該当する文字列
上記二つの条件を同時に満たすにはどうすればいいでしょうか。
ここで、ゼロ幅というのが役に立ちます。
ゼロ幅というのは、^ や $ と同じで、
なにか特定の文字のことを表すわけではなく、
位置だけを判別するもののことです。
ゼロ幅を活用すれば、正規表現置換の際に、
「~という条件で検索するが、置換対象には入れない」という使い方が可能になります。
^ や $ もそうですよね。
検索対象にはなりますが、置換対象にはなりません。
今回は、文字列 (またはパターン) をゼロ幅として検索します。
例えば、
sample.txt というファイル名があるとします。
txt の拡張子をもつファイルのファイル名だけを置換する場合、
拡張子は変えたくないので、 (\.txt) をゼロ幅で検索する必要があります。
また、ゼロ幅とは、 ^ や $ と同じで、位置だけを判別するものです。
このケースでは、拡張子の前までを置換対象として検索する必要があります。
特定の文字列の前までを置換対象にする場合は、
ゼロ幅の先読みを使用します。
書式は以下のようになります。
.+(?=\.txt)
こうすれば、任意の文字が 1 文字以上で .txt の前まで、という正規表現になります。
条件にマッチする部分の前まで、を表します。
逆に、ファイル名はそのままで、拡張子だけを置換する場合、
(?<=.+\.)[\w]+$
こうすれば、(半角英数字 1 文字以上 + ピリオド) の後ろから行末まで、という正規表現になります。
これは、後読みといいます。条件にマッチする部分の後ろから、を表します。
ゼロ幅の先読み、ゼロ幅の後読みのそれぞれに、
さらに、肯定表現、否定表現があります。
- (?=[a-z]+) // ゼロ幅の肯定的先読み - アルファベット小文字の後ろから
- (?![a-z]+) // ゼロ幅の否定的先読み - アルファベット小文字ではない文字の前から
- (?<=[a-z]+) // ゼロ幅の肯定的後読み - アルファベット小文字の前まで
- (?<![a-z]+) // ゼロ幅の否定的後読み - アルファベットの小文字ではない文字の後ろまで
さて、ここまでが、 ?! の部分の説明になります。
元々必要だった要件は、
index にも list にも相当しないディレクトリ名の前からを対象としますので、
ゼロ幅の否定的先読みを選択します。
よって、以下のようになります。
(?!(list|index)(/|$))
よって、URL の第一階層のディレクトリ名が index でも list でもなく、
なおかつ、英数字、 スラッシュ、 ピリオドで構成される文字列を
正規表現でマッチさせるためには、
^(?!(list|index)(/|$))[\w/\.]+
このような表記になるというわけです。
Zend_Controller_Router_Route_Regex などでも活用できそうです。
0 件のコメント:
コメントを投稿