Help us understand the problem. What is going on with this article?

PCRE関数での$matchesの扱いについて

More than 5 years have passed since last update.

正規表現に関する問題です。
次の出力結果を答えてください。

preg_match('/^(?:(a)|(b)|(c))$/', 'b', $matches);
var_dump($matches);

※ パターン (?:  ) を使うと、$matchesに代入されないグループを作成することが出来ます。

1
array(3) {
  [0]=>
  string(0) ""
  [1]=>
  string(1) "b"
  [2]=>
  string(0) ""
}
2
array(3) {
  [0]=>
  string(1) "b"
  [1]=>
  string(0) ""
  [2]=>
  string(1) "b"
  [3]=>
  string(0) ""
}
3
array(3) {
  [0]=>
  string(0) "b"
  [1]=>
  string(1) ""
  [2]=>
  string(0) "b"
}

(回答は↓) 
 
 
 
 
 
 
 
 
 
 
 
 
 
  
 
 
 
 
 
 
 
 
 
 
 
まあ 1 は「違う」って多くの人が納得されると思います。
そうです、 $matches[0]パターン全体 を表すからです。
今回の場合、
a → $matches[1]
b → $matches[2]
c → $matches[3]
となります。

残ってくる選択肢が 23
さて正解は?
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

実は 3 なんです。
「b」がマッチした時点で (?:(a)|(b)|(c)) 内の評価が終わり、3番目の (c) のキャプチャは作成されない、という理由です。(意外と目から鱗だった方もいらっしゃるのではないでしょうか?)
  
この仕様を知っておけば、こういう処理も楽々1回の正規表現で書けちゃいますね。

フィーチャーフォンのキャリア判定
$agent = isset($_SERVER['HTTP_USER_AGENT']) ? ($_SERVER['HTTP_USER_AGENT']) : '';
$pattern = '@'.
    '^DoCoMo/[12]\\.0|'.
    '(^(?:J-PHONE|Vodafone|MOT-[CV]980|SoftBank)/)|'.
    '(^KDDI-|UP\\.Browser/)|'.
    '(^PDXGW|DDIPOCKET;|WILLCOM;)'.
'@';
switch (true) {
    case !preg_match($pattern, $agent, $matches):
        echo 'その他';
        break;
    case !isset($matches[1]):
        echo 'DoCoMo';
        break;
    case !isset($matches[2]):
        echo 'Softbank';
        break;
    case !isset($matches[3]):
        echo 'au';
        break;
    default:
        echo 'WILLCOM';
}

このコードの作成にあたっては、こちらの情報を参考にしました。
ちなみに私は、

  • 正規表現エンジンに対しての不要なエスケープは行わない
  • バックスラッシュに関してはPHP言語としての問題になってくるので全てエスケープ

こういうポリシーのもとに書いています。
http://qiita.com/mpyw/items/8dd5378cb01c877e1f7b
  

  
さて今回はこのぐらいで。
・・・と思ったけど preg_match 以外の他の関数にも触れておきます。

  • preg_match_allPREG_SET_ORDER のとき
  • preg_replace_callback

この2つに関しては先ほどの preg_match と同じ認識でOKです。
ところが、

  • preg_match_allPREG_PATTERN_ORDER のとき

このときの挙動は異なります。

  • $matches[0][$i] が存在していれば、 $matches[1][$i] $matches[2][$i] $matches[3][$i] ・・・は必ず存在する
  • 実際にマッチしたものが無ければそれには空文字列が代入される

一言でいえば、
親配列($matches)が持つ子配列の要素数は必ず等しくなる

ちなみに

  • preg_replace

これに関してはもっと制約が緩いです。
例えば ( ) が3つしかないのに、 $4 とか書いちゃっても問題なく空文字列として処理してくれます。

mpyw
古い記事はそのまま参考にしないようにご注意ください
synapse
Synapseは、オンラインサロンサービスにおけるパイオニアとして、かつて存在していたスタートアップです。
https://synapseam.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away