somemo programming etc.

プログラマ、雑記、プログラミング関係はLinkから、数式はこっちでまとめていることが多い

【php】preg_match【正規表現】

パラメータ付きルーティングを作成するために、preg_matchで正規表現のおさらいをします。

REST

よく目にしますね。URIで何を示しているか、リソース志向であったり、RESTfullなど関連する言葉も多数あります。

自分が最初に感じたのは、URIに対してとても直感的であることでした。HTTPメソッドも絡んでいることは知らなかったので、同じURIでもメソッドによってサーバで処理される内容が違います。

例えば、domain/user/ユーザ名 などですね。これをGETでアクセスするとユーザに関する情報を取得できます。今までは、domain/user/Detail?user_name=ユーザ名 といった感じでした。

RESTについては詳しくないですが、ぜひ学んでみたいと思います。

正規表現

実装するためには、正規表現を使います。そこでpreg_matchを使っておさらい・・・。

<?php

dumpMatchingResult('(b|d)',           'abcde');
dumpMatchingResult('/(b|d)/',         'abcde');
dumpMatchingResult('/(a|b)c(de|fg)/', 'bcde');
dumpMatchingResult('/(?:b|d)/',       'abcde');
dumpMatchingResult('/(b|d)/',         'abcde', true);

umpMatchingResult('/(?Pa|b)c(?Pde|fg)/', 'bcde');

function dumpMatchingResult($pattern, $target, $all = false)
{
    $matching_func = $all ? 'preg_match_all'
                          : 'preg_match';
    
    $match_count = $matching_func($pattern, $target, $matches);

    printf('pattern:%s, target:%s, match_count:%d',
            $pattern,   $target,   $match_count);

    echo "\n";
    echo 'matches: ';
    print_r($matches);
    echo "\n";
}

preg_matchの戻り値はマッチングした件数です。ただし、preg_matchのマッチング回数は1回ですので、複数回マッチングさせたいときのためにallを用意しました。

それぞれ分けて解説します。

キャプチャ

()で囲まれたものは、マッチングした結果として参照することができます。グループ化として役割しか知りませんでした。以下、1つ目の実行結果です。

pattern:(b|d), target:abcde, match_count:1
matches: Array
(
    [0] => b
)

第三引数にはマッチング結果が格納されます。0番目にマッチングした全体の文字列、それ以降にはマッチングごとの文字列が格納されます。この「それ以降」がキャプチャ結果です。

abcdeには、bが存在します。よって、マッチング全体の文字列はbになり、キャプチャ結果はbになるはずです。しかし、キャプチャされていないようです。

デリミタ

いまさらなのですが、正規表現パターンの両端をスラッシュ「/」で囲みますよね。あれがデリミタです。この両端の文字に囲まれたものをパターンとして認識します。

これは、PCRE - Perl Compatible Regular Expressions( Perl互換正規表現)の決まりです。preg系は、これを示しています。またデリミタとして使われる文字には、ナンバー「#」やチルダ「~」を使うことが多いようです。パーフェクトPHPでは「#」を使っていました。

つまりキャプチャされなかった理由は、()がデリミタとして扱われたためです。2つ目は、デリミタにスラッシュが正しく適用されているのでキャプチャされました。

pattern:/(b|d)/, target:abcde, match_count:1
matches: Array
(
    [0] => b
    [1] => b
)

全体と各マッチングが同じだと分かりにくいので、3つ目に異なるものの場合を用意しました。以下のようになります。

pattern:/(a|b)c(de|fg)/, target:bcde, match_count:1
matches: Array
(
    [0] => bcde
    [1] => b
    [2] => de
)

パターン全体にマッチした文字列と、各マッチングの結果が表示されました。

キャプチャしたくない

()を使ってグループ化したいが、キャプチャしたくないときは4つ目のように「?:」を「(」の次に記述します。

pattern:/(?:b|d)/, target:abcde, match_count:1
matches: Array
(
    [0] => b
)

複数回マッチング

5つ目のallを使った結果です。最初の0番目に各マッチング時のマッチング全体文字列が格納されています。あとは、同じです。

pattern:/(b|d)/, target:abcde, match_count:2
matches: Array
(
    [0] => Array
        (
            [0] => b
            [1] => d
        )

    [1] => Array
        (
            [0] => b
            [1] => d
        )

)

後方参照

せっかくキャプチャできた値も何に対応したかを把握できなくては、意味がありません。対応付けを行うための書き方が6つ目です。結果は、以下のようになります。

pattern:/(?Pa|b)c(?Pde|fg)/, target:bcde, match_count:1
matches: Array
(
    [0] => bcde
    [first] => b
    [1] => b
    [second] => de
    [2] => de
)

「<>」で囲んだ名前の要素が作成されています。これをパラメータとして使います。また、これらを作成できるように、「:パラメータ名」のような定義を(?P<パラメータ名>)というパターンに変更する必要があります。これで準備はできたので、作成してみたいと思います。