Movatterモバイル変換


[0]ホーム

URL:


A Day in Serenity @ kenjis

この広告は、90日以上更新していないブログに表示しています。

PHP の正規表現があまりに複雑なのでまとめてみた

できるだけ正確な記述を目指していますが、誤りがありましたら、お知らせ願います。

(最終更新: 2013/3/29 11:22)

正規表現の種類

まず、PHP には以下の 3種類の正規表現があります。

  1. Perl 互換の正規表現 (pcre)
  2. mbstring の正規表現 (mbregex)
  3. POSIX拡張正規表現 (regex)

このうち、regex

  • バイナリセーフでない
  • 日本語は扱えない
  • PHP 5.3 で非推奨

なので使わない方がいいでしょう。見つけたら、随時 pcre か mbregex で書き直しましょう。

Perl 互換の正規表現 (pcre)

mbstring の正規表現 (mbregex)

パターンの書き方

正規表現パターンは文字列として記述します。PHP には正規表現リテラルがありません。

  • 文字列なので「'」で囲む (「"」だと変数展開されるなどがありややこしい)
  • PHP での「'」で囲んだ場合のエスケープ は以下になる
    • 「'」をリテラルとして指定するには、「\」でエスケープする
    • 「\」をリテラルとして指定するには、「\\」にする
    • それ以外の場面で登場する「\」は、すべて「\」そのものとして扱われる

ややこしいですが、パターンの文字列を echo して表示された結果が、正規表現エンジンに渡されるパターンになります。

$ cat test.php <?phpecho '/\\\\/', PHP_EOL;$ php test.php /\\/
  • pcre ではパターンをデリミタ (通常は「/」) で囲む必要がある

pcre での日本語の処理

pcre で日本語を処理する場合は、文字エンコーディングUTF-8 にし、パターン修飾子 u を付ける必要があります。付けないとマルチバイト文字が適切に処理されず、意図した結果を得られない場合があります。

<?phpif(PHP_OS=== 'WIN32'||PHP_OS=== 'WINNT'){setlocale(LC_ALL, 'C');}else{if(setlocale(LC_ALL, 'ja_JP.UTF-8')===false){exit('Can\'t set locale to UTF-8');}}$str= 'aあc';var_dump(preg_match('/a.c/',$str));var_dump(preg_match('/a.c/u',$str));$str= 'いう';var_dump(preg_match('/あ?いう/',$str));var_dump(preg_match('/あ?いう/u',$str));

上記の結果は、以下のようになります。

int(0)int(1)int(0)int(1)

文字クラス

\w, \d, \s について確認してみましょう (PHP 5.3.21)。

まず、\w の全角の漢字、かな、英数。

<?phpif(PHP_OS=== 'WIN32'||PHP_OS=== 'WINNT'){setlocale(LC_ALL, 'C');}else{if(setlocale(LC_ALL, 'ja_JP.UTF-8')===false){exit('Can\'t set locale to UTF-8');}}mb_regex_encoding('UTF-8');var_dump(preg_match('/\w/u', ''));var_dump(mb_ereg('\w', ''));var_dump(preg_match('/\w/u', ''));var_dump(mb_ereg('\w', ''));var_dump(preg_match('/\w/u', ''));var_dump(mb_ereg('\w', ''));var_dump(preg_match('/\w/u', ''));// 全角のAvar_dump(mb_ereg('\w', ''));var_dump(preg_match('/\w/u', ''));// 全角の1var_dump(mb_ereg('\w', ''));

この結果は、以下のようになります。

int(1)int(1)int(1)int(1)int(1)int(1)int(1)int(1)int(1)int(1)

全角の記号。

<?phpif(PHP_OS=== 'WIN32'||PHP_OS=== 'WINNT'){setlocale(LC_ALL, 'C');}else{if(setlocale(LC_ALL, 'ja_JP.UTF-8')===false){exit('Can\'t set locale to UTF-8');}}mb_regex_encoding('UTF-8');var_dump(preg_match('/\w/u', ' '));// 全角スペースvar_dump(mb_ereg('\w', ' '));var_dump(preg_match('/\w/u', ''));// 全角の感嘆符var_dump(mb_ereg('\w', ''));var_dump(preg_match('/\w/u', ''));var_dump(mb_ereg('\w', ''));var_dump(preg_match('/\w/u', '_'));// 全角のアンダースコアvar_dump(mb_ereg('\w', '_'));

この結果は、以下のようになります。

int(0)bool(false)int(0)bool(false)int(0)bool(false)int(0)int(1)

全角のアンダースコアは、pcre では \w にマッチしませんが、mbregex ではマッチします。

半角カナ。

<?phpif(PHP_OS=== 'WIN32'||PHP_OS=== 'WINNT'){setlocale(LC_ALL, 'C');}else{if(setlocale(LC_ALL, 'ja_JP.UTF-8')===false){exit('Can\'t set locale to UTF-8');}}mb_regex_encoding('UTF-8');var_dump(preg_match('/\w/u', ''));// 半角カナvar_dump(mb_ereg('\w', ''));

この結果は、以下のようになります。

int(1)int(1)

次に \d です。

<?phpif(PHP_OS=== 'WIN32'||PHP_OS=== 'WINNT'){setlocale(LC_ALL, 'C');}else{if(setlocale(LC_ALL, 'ja_JP.UTF-8')===false){exit('Can\'t set locale to UTF-8');}}mb_regex_encoding('UTF-8');var_dump(preg_match('/\d/u', ''));// 全角の1var_dump(mb_ereg('\d', ''));var_dump(preg_match('/\d/u', ''));// 漢数字の1var_dump(mb_ereg('\d', ''));

この結果は、以下のようになります。

int(1)int(1)int(0)bool(false)

全角数字は \d にマッチしますが、漢数字はマッチしません。

最後に \s です。

<?phpif(PHP_OS=== 'WIN32'||PHP_OS=== 'WINNT'){setlocale(LC_ALL, 'C');}else{if(setlocale(LC_ALL, 'ja_JP.UTF-8')===false){exit('Can\'t set locale to UTF-8');}}mb_regex_encoding('UTF-8');var_dump(preg_match('/\s/u', ' '));// 全角スペースvar_dump(mb_ereg('\s', ' '));

この結果は、以下のようになります。

int(1)int(1)

続いて、mbregex での \w の文字エンコーディングの違いについてもみておきましょう。

<?php$provider=array(// 全角漢字、英数、ひらがな、カタカタ    '', '', '', '', '',// 全角記号    '', '_',// 全角スペース    ' ',// 半角カナ    '',);echo 'mbregex: UTF-8 SJIS EUC-JP'. PHP_EOL;foreach($provideras$str){echo$str. ':';mb_regex_encoding('UTF-8');$test=mb_ereg('\w',$str);if($test===false)$test= '0';echo$test. '';mb_regex_encoding('SJIS');$str_s=mb_convert_encoding($str, 'SJIS', 'UTF-8');$test=mb_ereg('\w',$str_s);if($test===false)$test= '0';echo$test. '';mb_regex_encoding('EUC-JP');$str_e=mb_convert_encoding($str, 'EUC-JP', 'UTF-8');$test=mb_ereg('\w',$str_e);if($test===false)$test= '0';echo$test. '';echo PHP_EOL;}

この結果は、以下のようになります。

mbregex: UTF-8 SJIS EUC-JP亜: 1 1 1 A: 1 1 1 1: 1 1 1 あ: 1 1 1 ア: 1 1 1 !: 0 1 1 _: 1 1 1  : 0 1 1 ア: 1 0 1

SJISEUC-JP では、全角記号も \w にマッチします。半角カナはSJIS では \w にマッチしません。

\d はどうでしょうか?

<?php$provider=array(// 全角漢字、英数、ひらがな、カタカタ    '', '', '', '', '',// 全角記号    '', '_',// 全角スペース    ' ',// 半角カナ    '',);echo 'mbregex: UTF-8 SJIS EUC-JP'. PHP_EOL;foreach($provideras$str){echo$str. ':';mb_regex_encoding('UTF-8');$test=mb_ereg('\d',$str);if($test===false)$test= '0';echo$test. '';mb_regex_encoding('SJIS');$str_s=mb_convert_encoding($str, 'SJIS', 'UTF-8');$test=mb_ereg('\d',$str_s);if($test===false)$test= '0';echo$test. '';mb_regex_encoding('EUC-JP');$str_e=mb_convert_encoding($str, 'EUC-JP', 'UTF-8');$test=mb_ereg('\d',$str_e);if($test===false)$test= '0';echo$test. '';echo PHP_EOL;}

この結果は、以下のようになります。

mbregex: UTF-8 SJIS EUC-JP亜: 0 0 0 A: 0 0 0 1: 1 0 0 あ: 0 0 0 ア: 0 0 0 !: 0 0 0 _: 0 0 0  : 0 0 0 ア: 0 0 0

\d の場合は、全角数字はUTF-8 でしかマッチしません。

結局、どうすればいいのか?

setlocale() でロケールを設定します。

<?phpif(setlocale(LC_ALL, 'ja_JP.UTF-8')===false){exit('Can\'t set locale to UTF-8');}

Windows ではロケールUTF-8 にする方法がないようなので、どうするのが正しいのかはわかりません。知ってる人がいましたら、お教え願いたいです。

pcre では、UTF-8 の場合は、'/abc/u' のように必ず「u」を指定します。

mbregex では、mb_regex_encoding() で文字エンコーディングを指定し、必要があれば mb_regex_set_options() でオプションを変更します。

例えば、半角数字を期待するなら \d ではなく [0-9] を使います。

最後に、正常系、異常系についてユニットテストを書いておきます。そうすれば、自分の思い違い、正規表現エンジンの違いによる微妙な違い、万一PHP の仕様が突然変わった場合も発見できます。

検索

引用をストックしました

引用するにはまずログインしてください

引用をストックできませんでした。再度お試しください

限定公開記事のため引用できません。

読者です読者をやめる読者になる読者になる

[8]ページ先頭

©2009-2025 Movatter.jp