先日、入力された郵便番号の形式が正しいかを判定するコードをPHPで書いたのだが、思ったように動作しないことがあった。

以下がサンプルコードで、正規表現のマッチ条件は/\A\d{3}-?\d{4}\z/u

ハイフン有無どちらにも対応した郵便番号の正規表現だ。

<?php
$zip1 = "000-0000"; // 半角

echo preg_match('/\A\d{3}-?\d{4}\z/u', $zip1) ? '半角の郵便番号です' : '半角の郵便番号ではありません';

// 半角の郵便番号です

通常、このコードで問題なさそうに見えるが、実はこのコード、以下のように全角数字で入力があった場合にもマッチしてしまう。

<?php
$zip2 = "000-0000"; // 全角

echo preg_match('/\A\d{3}-?\d{4}\z/u', $zip2) ? '半角の郵便番号です' : '半角の郵便番号ではありません';

// 半角の郵便番号です

なぜこのようなことが起こるかというと、正規表現末尾に与えたuオプションにより、全角数字にもマッチするにようなっているのだ。

uオプションは、正規表現をUnicode(UTF-8)として解釈させるもので、日本語を扱うプログラムでは頻繁に利用されるオプションだ。

例えば先ほどのコードからuオプションを除くと、想定どおり半角数字にしかマッチしなくなるが、日本語を含むもう少し複雑な条件だとuオプションを除きたくない場合がある。

<?php
$zip2 = "000-0000"; // 全角

echo preg_match('/\A\d{3}-?\d{4}\z/', $zip2) ? '半角の郵便番号です' : '半角の郵便番号ではありません';

// 半角の郵便番号ではありません

uオプションの有無を問わず、最も確実に半角数字にマッチさせたい場合は、次のように\dを[0-9]に書き直すと良い。

<?php
$zip2 = "000-0000"; // 全角

echo preg_match('/\A[0-9]{3}-?[0-9]{4}\z/u', $zip2) ? '半角の郵便番号です' : '半角の郵便番号ではありません';

// 半角の郵便番号ではありません

これならばuオプションがあっても、正しく半角と全角を切り分けてマッチしてくれるようになる。