PHPの正規表現
2015-06-22 GMOリサーチ 寺田渉
facebook: 寺田渉
github: waterada
twitter: @wa_terada
こう書くといいよ
自己紹介 (会社)
- PHP (CakePHP) / java (Spring MVC) を主に使って開発
- 継続的インテグレーション
- github + git flow で運用
- PHPUnit / JUnit で カバレッジ 100%
- Behat (Selenium Driver 経由の画面テスト) 利用
- vagrant で開発環境構築
自己紹介 (趣味)
CakePHP 公式ドキュメント 翻訳
自己紹介 (趣味)
ボードゲーム 翻訳
自己紹介 (趣味)
TED 翻訳
自己紹介
プログラミング & 翻訳
大好き人間です
「正規表現」
知ってます?
で、本題。
こういうやつ
if (preg_match('/[^0-9]/', $str)) {
//正の整数じゃないよエラー
}
ある文字列が定義したパターン(=正規表現)に
合致するかどうかを簡単にチェックできます。
置き換えもできます。
知らないなら すぐに
調べた方がいいです
絶対効率よい
でも使いすぎはダメ。確認しづらい。
上司: メアドチェックするロジック作っといて!
新人: 出来ました!
たとえば・・・
合ってるかどうかじゃありません。
メンテしていけるかどうかです。
if(preg_match('/^(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){255,})(?!(?:(?:
\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){65,}@)(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2
F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C
[\x00-\x7F]))*\x22))(?:\.(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(
?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22)))*@(?:(?:(?!.*[
^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--
)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?
:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1
,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})
(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:
25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1
[0-9]{2})|(?:[1-9]?[0-9]))){3}))\]))$/iD', $email)) {
メアドチェックする正規表現:
これ来たら、つらいですよね?
前置きはこのくらいにして、
を紹介していきます。
こう書くと いいよ / ダメだよ
デリミタ変えれるよ
$html = preg_replace('/^(\/[^\/]+)+\//', '', $html);
/aa/bb/cc/dd → dd
$html = preg_replace('{^(/[^/]+)+/}', '', $html);
$html = preg_replace('#^(/[^/]+)+/#', '', $html);
$ は終端という意味じゃない 【\z】
if (! preg_match('/^[0-9]+$/', $str)) {
//数字以外ならエラー
$str = "123\n" でもOKとなってしまう! ($ は終端 or 終端の改行という意味なので)
if (! preg_match('/^[0-9]+\z/', $str)) {
//数字以外ならエラー
2015-06-24 イカID:yuyat.jpさんの指摘がとても良かったのでこのページにて共有させて頂きました。
$str = "123\n" でも意図通りエラーとなる!
(\z は終端という意味なので)
非貪欲マッチ 【?】
$html = preg_replace('#<b>.*</b>#', '', $html);
AA<b>BB</b>CC<b>DD</b>EE → AAEE
$html = preg_replace('#<b>.*?</b>#', '', $html);
AA<b>BB</b>CC<b>DD</b>EE → AACCEE
―――――――
―――
―――
2015-06-24 @hnwさんの指摘で「最小」→「非貪欲」に直させて頂きました。
. は基本改行含めない 【s】
if (preg_match('/<img.*?>/', $str)) {
"<img
>" → 合致しない
if (preg_match('/<img.*?>/s', $str)) {
"<img
>" → 合致する
マルチライン 【m】
$str = preg_replace('/^|(\n)/', '$1- ', $str);
結果:
$str = preg_replace('/^/m', '- ', $str);
- aa - bb -
結果:
- aa - bb
各行の先頭に「- 」を
後方参照がたくさんあるなら
コメントあると解りやすい
// 1 2 3 4 5 6
$pattern = '/^(.*?),(.*?),(.*?),(\d+)/(\d+)/(\d+)$/';
$replace = '$6-$5-$4 $2:$3:$1';
$str = preg_replace($pattern, $replace, $str);
\Q と \E で見やすく
$str = preg_replace('/\(\*\^-\^\*\)/', '', $str);
$str = preg_replace('/\Q(*^-^*)\E/', '', $str);
\Q と \E を ユーザ入力に使っちゃダメ
// $name = "xxxx-abcd";
if (preg_match('/\Q'.$input.'\E/', $name)) {
// $input に '\E' が入っていると困る
if (preg_match('/'.preg_quote($input,'/').'/', $name)) {
x 使って わかりやすく書く
if (preg_match("/^-?(?:[1-9]\d*|0)(?:\.\d+[1-9])?$/",
$num)) {
if (preg_match("/
^ # 先頭
-? # マイナスは有っても無くても良い
(?:
[1-9] # 整数部一桁目は0禁止
\d* # 整数部
|
0 # 0 のみ整数部の1桁目が 0 でも良い
)
(?:
\. # 小数点
\d* # 小数部
[1-9] # 小数部の末尾は 0 禁止
)? # 小数部は無くてもいい
$ # 末尾
/x", $num)) {
言明は使わないほうが身のため
言明とは下記のようなやつ。 (?= (?! (?<= (?<!
マッチするけど結果に含めないとかいうやつです。
メンバー全員がテストケース洗い出せるくらいでないと
バグの温床になります!
(?: ) は ( ) とほぼ同じ
// 1 2 3
$pattern = '/^(?:.*?),(?:.*?),(?:.*?),(\w+)/(\w+)/(\w+)$/';
$replace = '$3-$2-$1';
$str = preg_replace($pattern, $replace, $str);
(?: ) と ( ) の違いは $1 として 後で参照するか 読みやすくなると思ったら使おう
応用編
何が問題?
# <script>~</script>を撤去して出力
# $str = "[<script>alert(1);</script>]";
# なら
# []
# が出力される。
echo preg_replace("#<script>.*?</script>#", "", $str);
この正規表現には抜け穴があり、下記を出力できる。
[<script>alert(1);</script>]
$str がどんな文字のときにそうなる?
答え
$str = "[<scr<script></script>ipt>alert(1);</script>]";
echo preg_replace("#<script>.*?</script>#", "", $str);
このように置き換え後の文字は生き残るのだ。
[<script>alert(1);</script>]
これで下記が出力される:
以上です!
感想などあれば:
facebook: 寺田渉
github: waterada
twitter: @wa_terada
ご静聴ありがとうございました!
PHPの正規表現こう書くといいよ
By Wataru Terada
PHPの正規表現こう書くといいよ
- 11,628