電話番号の正規表現はどうしたらいいのか

Railsのアプリを作っていて電話番号のバリデーションをするための正規表現はどう書いたらいいのか悩んだので調べてみました

※細かくやっていますが新しい電話番号や考慮されないパターンがあるかもなのでバリデーションは緩くしておくことをおすすめします。

電話番号の正規表現

日本の一般の電話番号の正規表現はこうなりそう

/\A(((0(\d{1}[-(]?\d{4}|\d{2}[-(]?\d{3}|\d{3}[-(]?\d{2}|\d{4}[-(]?\d{1}|[5789]0[-(]?\d{4})[-)]?)|\d{1,4}\-?)\d{4}|0120[-(]?\d{3}[-)]?\d{3})\z/

想定している仕様

  • 0から始まる市外局番込の10桁の番号
  • [2-9]から始まる市外局番を含まない5~8桁の番号
  • 市外局番と市内局番は各1~4桁、合わせると5桁 になる
  • 携帯電話とPHSは「070」、「080」又は「090」から始まる11桁の番号
  • 市外局番、市内局番の境目で区切りが入る可能性がある 03-1234-1234
  • 区切りが市内局番を()で囲む可能性がある 03(1234)1234
  • フリーダイヤルは0120から始まる4桁3桁3桁

もしくは分けて

# 市外局番ありパターン
/\A0(\d{1}[-(]?\d{4}|\d{2}[-(]?\d{3}|\d{3}[-(]?\d{2}|\d{4}[-(]?\d{1})[-)]?\d{4}\z/

# 市外局番なしパターン
/\A\d{1,4}\-?\d{4}\z/

# 携帯電話パターン
/\A0[5789]0[-(]?\d{4}[-)]?\d{4}\z/

# フリーダイヤルパターン
/\A0120[-(]?\d{3}[-)]?\d{3}\z/

を順次チェックしてマッチしなければという使い方でも分かりやすいかも

以下はこの正規表現にたどり着くまでの調査結果などです。

色々なサイトで電話番号の正規表現が違う

電話番号のバリデーションがしたく正規表現を探したけれどサイトごとにバラバラ…
なんなら考慮されていないパターンがあったり…

# 【電話番号】半角数字で入力されているか
/^[0-9]+$/

# 【電話番号】半角ハイフンを含んだ1〜4桁・1〜4桁・3〜4桁の半角数字の形式で入力されているか
/^0\d{1,4}-\d{1,4}-\d{3,4}$/

http://designsupply-web.com/knowledgeside/1640/

色んなパターン書いて結局どれ使うんだっていうのが悩ましいサイトやら

/^¥d{3}-¥d{4}$|^¥d{3}-¥d{2}$|^¥d{3}$/
/^¥d{3}¥-¥d{4}$/

/^¥d{3}-¥d{4}-¥d{4}$|^¥d{11}$/
/^0¥d0-¥d{4}-¥d{4}$/

/^[0-9-]{6,9}$|^[0-9-]{12}$/
/^¥d{1,4}-¥d{4}$|^¥d{2,5}-¥d{1,4}-¥d{4}$/

http://befine.jugem.jp/?eid=29

03-1234-1234 だけでなく 03(1234)1234 も通すパターンがあったり

/\d{2,5}[-(]\d{1,4}[-)]\d{4}/

http://qiita.com/jnchito/items/893c887fbf19e17d3ff9#%E9%9B%BB%E8%A9%B1%E7%95%AA%E5%8F%B7%E3%82%92%E6%8E%A2%E3%81%99

そのそも電話番号のパターンはどういうのがあるのか

基本的な仕様

総務省が電話番号に関しての説明を公開していました。

f:id:kuronekopunk:20170531181426p:plain

総務省|電気通信番号の利用・指定|電話番号に関するQ&A

国内の一般の電話番号だけであれば

  • 0から始まる市外局番込の番号
  • [2-9]から始まる市外局番を含まない番号

の2パターンでよさそう

また市外局番+市内局番は5桁と決まっている模様

携帯電話

携帯電話とPHSは「070」、「080」又は「090」から始まる11桁の番号です。

総務省|電気通信番号の利用・指定|電話番号に関するQ&A

9桁の電話番号?

電話番号が、頭の0と局番を含め9桁であるもの。現在では一つも残されていない。
かつて東京(03)と大阪(06)が、9桁の電話番号地域として有名だったが、番号の逼迫に対応するため、次のように一桁増やして10桁化するよう、変更されている。

電話番号の桁数 | ただの作業メモ

9桁の電話番号 ‐ 通信用語の基礎知識

仕様を確認して正規表現を作ってみる

  • 0から始まる市外局番込の10桁の番号
  • [2-9]から始まる市外局番を含まない5~8桁の番号
  • 市外局番と市内局番は各1~4桁、合わせると5桁 になる
  • 携帯電話とPHSは「070」、「080」又は「090」から始まる11桁の番号
  • 市外局番、市内局番の境目で区切りが入る可能性がある 03-1234-1234
  • 区切りが市内局番を()で囲む可能性がある 03(1234)1234

まず市外局番込の10桁の正規表現を作る

必要そうなのはこのあたり

  • 0から始まる市外局番込の10桁の番号
  • 市外局番と市内局番は各1~4桁、合わせると5桁 になる
  • 市外局番、市内局番の境目で区切りが入る可能性がある 03-1234-1234
  • 区切りが市内局番を()で囲む可能性がある 03(1234)1234

想定パターン

  • 01-1234-1234
  • 012-123-1234
  • 0123-12-1234
  • 01234-1-1234
  • 01(1234)1234
  • 012(123)1234
  • 0123(12)1234
  • 01234(1)1234
  • 0123456789
/\A0(\d{1}[-(]?\d{4}|\d{2}[-(]?\d{3}|\d{3}[-(]?\d{2}|\d{4}[-(]?\d{1})[-)]?\d{4}\z/

市外局番抜きもマッチするように変更

想定パターン

  • 1234-1234
  • 123-1234
  • 12-1234
  • 1-1234
  • 12345
/\A((0(\d{1}[-(]?\d{4}|\d{2}[-(]?\d{3}|\d{3}[-(]?\d{2}|\d{4}[-(]?\d{1})[-)]?)|\d{1,4}\-?)\d{4}\z/

050, 070, 080, 090から始まる11桁もマッチするように変更

/\A((0(\d{1}[-(]?\d{4}|\d{2}[-(]?\d{3}|\d{3}[-(]?\d{2}|\d{4}[-(]?\d{1}|[5789]0[-(]?\d{4})[-)]?)|\d{1,4}\-?)\d{4}\z/

フリーダイヤルもマッチするように変更

4桁3桁3桁

  • 0120-123-123
/\A(((0(\d{1}[-(]?\d{4}|\d{2}[-(]?\d{3}|\d{3}[-(]?\d{2}|\d{4}[-(]?\d{1}|[5789]0[-(]?\d{4})[-)]?)|\d{1,4}\-?)\d{4}|0120[-(]?\d{3}[-)]?\d{3})\z/
# rubyで検証
a = ['01-1234-1234','012-123-1234','0123-12-1234','01234-1-1234','01(1234)1234','012(123)1234','0123(12)1234','01234(1)1234','0123456789','1234-1234','123-1234','12-1234','1-1234','12345','050-1234-1234','070-1234-1234','080-1234-1234','090-1234-1234','0120-525-256','0120117117','1234567890','012345678','01234567890']

pattern = /\A(((0(\d{1}[-(]?\d{4}|\d{2}[-(]?\d{3}|\d{3}[-(]?\d{2}|\d{4}[-(]?\d{1}|[5789]0[-(]?\d{4})[-)]?)|\d{1,4}\-?)\d{4}|0120[-(]?\d{3}[-)]?\d{3})\z/

a.each {|e| puts e unless e =~ pattern }

# => 1234567890
# => 012345678
# => 01234567890

桁数が違うものや桁数が合っていても0から始まらないものだけが出力されることを確認しました。

もうこうれでいいんじゃないだろうか…