Menu

PHPの正規表現:preg_match()の使い方と例

preg_match($pattern, $subject, $matches)は文字列を正規表現と照合し、一致すれば1、しなければ0を返します。キャプチャグループ、名前付きグループ、preg_match_all、アンカー、入力検証のパターン、UTF-8や日本語のテキスト用のu修飾子を学びます。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

preg_match($pattern, $subject, $matches)は、文字列が正規表現に一致するかを確認します。一致すれば1を、そうでなければ0を返し、見つけたもので$matchesを埋めます:preg_match('/\d+/', 'Order 4521 shipped', $m)は1を返し、$m[0]を"4521"にします。

パターンは前後を区切り文字(たいてい/.../)で囲んだ文字列で、そのあとにiのような修飾子が続きます。パターンはシングルクォートで書きましょう。ダブルクォートの中では、PHPが先に$nameや、\x41や\101のようなエスケープを展開するので、正規表現エンジンが入力したものとは違うパターンを受け取ることがあります。

キャプチャグループ

パターンの中の括弧は、文字列のうち一致した部分を取り込みます。$m[0]が一致全体、$m[1]が最初のグループ、$m[2]が2つ目で、左から開き括弧の順に数えます。

名前付きグループ

(?<name>...)はグループに名前を付けます。値は名前と番号の両方で保存されるので、誰かがパターンの前のほうにグループを追加しても、結果を読むコードは壊れません。

preg_match_allですべての一致を見つける

preg_match_all()は最初の一致のあとも検索を続け、一致の数を返します。デフォルトでは$m[0]にすべての一致全体が、$m[1]に最初のグループのすべての値が入ります。PREG_SET_ORDERを指定すると、代わりに一致ごとに1つの配列が得られ、ループしやすくなります。

入力を検証する:アンカー、電話番号、郵便番号

検証では、パターンが文字列全体に一致する必要があるので、^で始めて$で終えます。罠が1つあります。$は末尾の改行の直前にも一致するので、'/^\d+$/'は"123\n"を受け付けてしまいます。D修飾子を付けるか、本当の末尾にだけ一致する\zを使いましょう。

メールアドレスでは、正規表現は正しいアドレスを拒否するか、不正なアドレスを受け付けるかのどちらかになります。代わりにfilter_var($email, FILTER_VALIDATE_EMAIL)を使いましょう(filter_var()を参照)。

UTF-8や日本語のテキスト用のu修飾子

u修飾子がないと、パターンはバイトに対して働きます。日本語の文字はUTF-8で3バイトなので、.は文字の3分の1に一致し、長さも間違ってしまいます。uを付けるとパターンは文字に対して働き、Unicodeのプロパティクラスも使えます。

uは対象の文字列の検証も行います。文字列が正しいUTF-8でなければ、preg_match()は一致する代わりにfalseを返します。

preg_quoteでユーザーの入力をエスケープする

.、+、?、(、/のような文字は、パターンの中で特別な意味を持ちます。検索語のようにパターンの一部が変数から来る場合は、2つ目の引数に区切り文字を指定してpreg_quote()に通しましょう。そうしないと1+1が11に一致し、エスケープされていない/がパターンを壊します。

不正なパターンは例外を投げません。PHPは警告を表示し、preg_match()はfalseを返します。

Warning: preg_match(): No ending delimiter '/' found in /path/to/index.php on line 3

そのため、パターンを実行時に組み立てるときは=== false(またはpreg_last_error_msg())で確認します。パターンを使わない単純な部分文字列の確認なら、str_contains()のほうが速くてわかりやすく書けます。一致したテキストを変更するにはpreg_replace()を使います。

よくある質問

PHPのpreg_matchは何を返しますか?

パターンが一致すれば1、一致しなければ0、パターン自体が不正ならfalseを返します。0もfalseもfalseと評価されるので、パターンの失敗と一致なしを区別する必要があるなら=== 1で比較します。

preg_matchで一致したテキストを取得するには?

3つ目の引数を渡します:preg_match('/(\d+)/', 'Order 4521', $m)。すると$m[0]が一致全体、$m[1]が最初のキャプチャグループで、ここではどちらも"4521"です。名前付きグループ(?<id>\d+)なら、値は$m['id']にも入ります。

preg_matchとpreg_match_allの違いは何ですか?

preg_match()は最初の一致で止まります。preg_match_all()はすべての一致を見つけて見つけた数を返し、$matchesをそのすべてで埋めます:preg_match_all('/\d+/', 'a1 b22 c333', $m)は3を返し、$m[0]は["1", "22", "333"]です。

日本語などのUTF-8テキストでpreg_matchを使うには?

u修飾子を付けます:preg_match('/^.{3}$/u', '日本語')は1を返します。uがないとPHPは文字列をバイトとして扱うので、.は3バイトの文字の1バイトに一致します。uを付ければ、\p{Han}、\p{Hiragana}、\p{Katakana}のようなUnicodeのクラスも使えます。

PHPで文字列が数字だけかを確認するには?

preg_match('/^\d+$/D', $s)、または正規表現なしでctype_digit($s)です。D修飾子(または$の代わりに\z)が重要です。素の$は末尾の改行も受け付けるので、"123\n"も通ってしまいます。

Coddyのプログラミング言語のイラスト

Coddyでコードを学ぼう

始める