PHP

電話番号の正規表現は?

電話番号のバリデーション(日本国内)を行う際の正規表現について。
よく見るやりこみ的な話ではなく実用レベルのものを解説していきます。

結論

結論から書くと、

PHP/Rubyなどは

// ハイフン無しなら
\A0\d{9,10}\z

// ハイフン有りなら
\A0[-\d]{11,12}\z

// ハイフンの有無問わずなら
\A0[-\d]{9,12}\z

JavaScript

// ハイフン無しなら
^0\d{9,10}$

// ハイフン有りなら
^0[-\d]{11,12}$

// ハイフンの有無問わずなら
^0[-\d]{9,12}$

になります。

\dは0-9の数字1文字を表します。\Aは文字列の先頭、\zは文字列の末尾を表します。
Rubyはデフォルトでマルチラインモードなので^(行の先頭)や$(行の末尾)が非推奨です(参考)が、
JavaScriptであれば\Aや\zがそもそも使えません。

正規表現としては上記で十分で、複雑なものは避ける方が無難です。

なぜ複雑なものは避ける方がよいか、以下で詳しく解説していきます。

何のための正規表現か?

おそらく、電話番号の正規表現と調べるとつらつら長い正規表現がでてくるかと思います。
しかしながら、よくわからないけど出てきた正規表現を使う、といったことをしてしまうと新しい電話番号が弾かれる可能性があります。

あなたに必要なのは何に使うための正規表現でしょうか?
「電話番号って色んなパターンあるけど正規表現にしたらどうなるのかな?」であれば複雑なものになります。
その場合は当記事では取り上げていないので他をご参照ください。
(ただし後述の通り詳細な正規表現は年々変わっていくので数年前の記事の正規表現は現在では使えません)

しかし、おそらく大半の人は、ただ入力のバリデーションのために正規表現を使いたいのかと思います。
なお、正規表現をどれだけがんばっても嘘の入力は弾けません。他人の正しい電話番号を入力される可能性があるからです。

ではバリデーションのために必要なものと不要なものは何でしょうか。

電話番号判定の条件

文字種:半角の数字であることや-(ハイフン)やカッコの有無
03-1234-5678なのか、0312345678なのか、もしくはちょっと特殊で03(1234)5678なのか
これは保存するデータ形式として必要なバリデーションです。
文字の種類は必ずバリデーションとして絞っておきましょう。

0から始まる
0以外から始まるものは国際番号や110などの特殊な番号になります。
03-1234-5678という番号があったとして、この番号は+81 3 1234 5678 と打つと海外からでも電話をかけることができます。
0から始まることを指定することで、自動的に国内の電話番号のみに限定することができます。

10~11桁である
電話番号は固定電話は10桁、携帯などは11桁です。(ハイフンなどの記号は除く)
ハイフンなどの記号が入る場合は2文字追加されるので12~13桁になります。
0-9の色々な数字が入る可能性があるのは、先頭の固定の0を除いてそれぞれ後ろの9~10桁、ハイフン有りなら11~12桁の部分です。

携帯の電話番号(=11桁の電話番号)は090,080,070のどれかから始まる
日本国内の携帯電話番号について"現在のものを正確に表す"のであればこの条件は必要です。
しかし、携帯の電話番号はもともと090だったところから番号が不足し始めたために080が追加され、それでも足りずPHS用だった070の割り当てを開始しています。
つまり、今後もまた新しい番号が割り当てられていく可能性があります。
そうなったときにバリデーションを書き換えていないと、新しい電話番号が割り当てられたお客さんから
「登録できないんだけど!」というお問い合わせがくることになるでしょう。
便利にするためのはずのバリデーションで不便な思いをしており本末転倒です。

ちゃんと電話番号のニュースについてアンテナをはって、変更があったときにすかさずバリデーション部分のメンテナンスをする体制がととのっているならいいですが...そんなことをするコストは地味ながら大きいですし、おそらくそんなことはしないでしょう。

なお、050から始まるIP電話の番号も11桁です。11桁なら携帯といった判定はできません。

✕ ハイフンの位置
ハイフンの区切りは後ろが4桁-4桁になるものだと思っていませんか。
和歌山県など3桁-3桁-4桁だったり、4桁-2桁-4桁、伊豆大島など5桁-1桁-4桁の電話番号もあります。

また、実は携帯電話番号は元々3桁-3桁-5桁の区切りで設定されており、真ん中の3桁で事業者がわかるようになっていました。
気になる方はCDEコードなどで調べてみてください。
今では見やすくするために一般的には3桁-4桁-4桁の表記がされています。

ハイフンの位置違いで違うところに電話がかかるというようなことはありません。
見やすくするために区切っているだけと割り切り、位置の正確さにはこだわらなくてよいでしょう。

番外編 0120や050を弾きたい
このような場合は、正規表現で判定するのとは別で弾くことをおすすめします。
無理に複雑な正規表現1つにしてもメリットは特になく、むしろ正規表現が複雑になってメンテナンスがしにくくなるためです。
また、別で弾けば「0120から始まる電話番号は登録できません」などとメッセージを出し分けて、弾かれた理由をわかりやすくすることで
ユーザーフレンドリーなUIにすることもできるでしょう。

簡単な正規表現は何か不安が

「めちゃくちゃ複雑な正規表現がいっぱい出てくるのに本当に簡単な正規表現でいいの?何か不安...」
という人も中にはいるかと思います。
そんな方は何のための正規表現なのかを一度考えてみましょう。

アルファベットやひらがなが入っているなど形式が違うのはもちろん困ります。
市外局番からいれてほしいのに市外局番がないのも困ります。

でもどれだけがんばっても、正規表現で打ち間違いを全てなくせるわけではありません
どこまでいっても、一箇所数字を間違って存在する別の電話番号を入力されてしまうという可能性が残るからです。
これを防ぐことができるのは正規表現ではなくSMSなどによる認証です。
残念ながら正規表現をどれだけがんばってもSMS認証の実装をサボれるわけではありません。

正規表現はあくまでおまけ、明らかに不正な形式を弾いたり一部のケースでユーザビリティを向上させる程度のものです。
複雑でメンテできない正規表現で登録できる人をはじいてしまう方がデメリットです。
ちゃんと目的を認識して必要なことを見極め、適した手段をとれるようになりましょう。

-PHP
-,