電話番号チェックは「“電話っぽい数字”だけを通す」ための技
業務システムでは、電話番号を入力してもらう場面が本当に多いです。
問い合わせフォーム、会員登録、配送先情報、緊急連絡先…。
ここでチェックをしないと、
「abc」「000」「123」みたいな、明らかに電話じゃない値がDBに入ってしまいます。
後で電話をかけようとしてもかからないし、オペレーターも困るし、データも汚れます。
そこで使うのが 電話番号チェック。
これは「この文字列が“電話番号としてあり得る形かどうか”をざっくり判定する」ためのユーティリティです。
完璧な判定ではなく、「明らかにおかしいものを早めにはじく」フィルタだと思ってください。
まずは「電話番号のざっくりルール」を決める
完璧を目指さず“現場で困らないライン”を決める
日本の電話番号だけを考えても、実はかなりバリエーションがあります。
03-1234-5678(固定電話)
090-1234-5678(携帯)
0120-123-456(フリーダイヤル)
050-1234-5678(IP電話)
さらに、ハイフンがあったりなかったり、国際番号が付いたりもします。
0312345678
+81-3-1234-5678
これを「全部きっちり判定しよう」とすると、正規表現が怪物になります。
なので、まずは “自分たちのシステムで許したい形”を決める ところから始めるのが現実的です。
例えば、こんな方針が考えられます。
数字とハイフンだけ許可する
ハイフンを抜いたときに、桁数が 10〜11 桁ならOK(日本の固定・携帯を想定)
先頭は 0 から始まるものだけ許可する(国内番号だけを想定)
このくらいでも、「明らかにおかしいもの」はかなり弾けます。
ステップ1:まずは「数字とハイフンだけ」に絞る
文字種チェックだけでもかなり効果がある
最初のフィルタとして、「数字とハイフン以外が混ざっていたらNG」にするだけでも、
かなりのゴミデータを防げます。
public final class PhoneNumberValidator {
private PhoneNumberValidator() {}
public static boolean containsOnlyDigitsAndHyphen(String phone) {
if (phone == null || phone.isBlank()) {
return false;
}
for (int i = 0; i < phone.length(); i++) {
char c = phone.charAt(i);
if (!(c >= '0' && c <= '9') && c != '-') {
return false;
}
}
return true;
}
}
Java使い方はこうなります。
System.out.println(PhoneNumberValidator.containsOnlyDigitsAndHyphen("03-1234-5678")); // true
System.out.println(PhoneNumberValidator.containsOnlyDigitsAndHyphen("09012345678")); // true
System.out.println(PhoneNumberValidator.containsOnlyDigitsAndHyphen("03-12A4-5678")); // false
System.out.println(PhoneNumberValidator.containsOnlyDigitsAndHyphen("03 1234 5678")); // false
System.out.println(PhoneNumberValidator.containsOnlyDigitsAndHyphen("")); // false
Javaここでの重要ポイントは、「“文字種の制限”だけでも立派なバリデーションになる」という感覚です。
いきなり正規表現で全部やろうとせず、
「まずは許可する文字を絞る」→「次に桁数を見る」→「さらに細かいルールを足す」
という段階的な発想を持つと、コードも読みやすくなります。
ステップ2:ハイフンを抜いて桁数をチェックする
「10〜11桁ならOK」というシンプルなルール
次に、「数字として何桁あるか」を見ていきます。
日本の固定電話と携帯電話を想定するなら、
ハイフンを抜いたときに 10 桁(固定)か 11 桁(携帯)ならOK、というルールがよく使われます。
public final class PhoneNumberValidator {
private PhoneNumberValidator() {}
public static boolean isDomesticPhoneNumber(String phone) {
if (phone == null || phone.isBlank()) {
return false;
}
// まずは文字種チェック
if (!containsOnlyDigitsAndHyphen(phone)) {
return false;
}
// ハイフンを除去
String digits = phone.replace("-", "");
// すべて数字かどうか(念のため再確認)
if (!digits.chars().allMatch(Character::isDigit)) {
return false;
}
// 桁数チェック(10 or 11)
int len = digits.length();
if (len != 10 && len != 11) {
return false;
}
// 先頭は 0 から始まる(国内番号想定)
if (!digits.startsWith("0")) {
return false;
}
return true;
}
private static boolean containsOnlyDigitsAndHyphen(String phone) {
for (int i = 0; i < phone.length(); i++) {
char c = phone.charAt(i);
if (!(c >= '0' && c <= '9') && c != '-') {
return false;
}
}
return true;
}
}
Java使い方はこうです。
System.out.println(PhoneNumberValidator.isDomesticPhoneNumber("03-1234-5678")); // true
System.out.println(PhoneNumberValidator.isDomesticPhoneNumber("090-1234-5678")); // true
System.out.println(PhoneNumberValidator.isDomesticPhoneNumber("0312345678")); // true
System.out.println(PhoneNumberValidator.isDomesticPhoneNumber("09012345678")); // true
System.out.println(PhoneNumberValidator.isDomesticPhoneNumber("1234567890")); // false(先頭が0でない)
System.out.println(PhoneNumberValidator.isDomesticPhoneNumber("03-123-5678")); // false(桁数が合わない)
System.out.println(PhoneNumberValidator.isDomesticPhoneNumber("03-1234-56789")); // false(桁数が多い)
System.out.println(PhoneNumberValidator.isDomesticPhoneNumber("+81-3-1234-5678"));// false(このルールではNG)
Javaここで深掘りしたい重要ポイントは三つです。
一つ目は、「ハイフンの有無に関係なく、“数字として何桁か”を見る」という発想です。
ユーザーはハイフンを入れたり入れなかったりするので、
ハイフンは一旦全部取り除いてから、数字としての桁数をチェックするのが素直です。
二つ目は、「桁数と先頭の数字で“だいたい日本の電話っぽいか”を判定している」ことです。
10 or 11 桁で、先頭が 0 なら、
固定電話・携帯・フリーダイヤルなど、ほとんどの国内番号をカバーできます。
三つ目は、「“国際番号付き”や“内線番号付き”などは、このルールではあえて対象外にしている」ことです。
すべてを1つのメソッドでカバーしようとせず、
「国内向けフォーム用のチェック」と割り切ることで、
コードをシンプルに保っています。
ステップ3:正規表現で「形」までチェックする
「ハイフンの位置」まで気にしたい場合
もう少し厳しく、「ハイフンの位置もある程度制限したい」という場合は、
正規表現を使うこともできます。
例えば、次のようなざっくりルールを考えます。
ハイフンなしの 10〜11 桁の数字
または数字2〜4桁-数字2〜4桁-数字4桁 の形
これを正規表現にすると、こんな感じになります。
import java.util.regex.Pattern;
public final class PhoneNumberValidator {
private PhoneNumberValidator() {}
private static final Pattern SIMPLE_JP_PHONE =
Pattern.compile("^(0\\d{9,10}|0\\d{1,3}-\\d{2,4}-\\d{4})$");
public static boolean isSimpleJapanesePhone(String phone) {
if (phone == null || phone.isBlank()) {
return false;
}
return SIMPLE_JP_PHONE.matcher(phone).matches();
}
}
Java使い方はこうです。
System.out.println(PhoneNumberValidator.isSimpleJapanesePhone("0312345678")); // true
System.out.println(PhoneNumberValidator.isSimpleJapanesePhone("03-1234-5678")); // true
System.out.println(PhoneNumberValidator.isSimpleJapanesePhone("090-1234-5678")); // true
System.out.println(PhoneNumberValidator.isSimpleJapanesePhone("0120-123-456")); // true(ざっくりOK)
System.out.println(PhoneNumberValidator.isSimpleJapanesePhone("03-12-5678")); // false
System.out.println(PhoneNumberValidator.isSimpleJapanesePhone("03-12345-678")); // false
System.out.println(PhoneNumberValidator.isSimpleJapanesePhone("1234567890")); // false
System.out.println(PhoneNumberValidator.isSimpleJapanesePhone("+81-3-1234-5678")); // false
Javaここでのポイントは、「正規表現で“形”までチェックすると、読みやすさと柔軟性のトレードオフが出てくる」ことです。
パターンを厳しくすればするほど、「この番号は本当は有効なのに弾かれてしまう」というケースも増えます。
どこまで厳しくするかは、システムの性質やユーザー層に合わせて決める必要があります。
「形式チェック」と「実在チェック」は別物だと理解する
メールアドレスと同じで、電話番号チェックも
「この番号に本当にかかるかどうか」は分かりません。
09012345678 が形式的に正しくても、
実際には誰も使っていない番号かもしれません。
実在チェックをしたいなら、
SMS認証や音声通話によるワンタイムパスコードなど、
別の仕組みが必要になります。
電話番号チェックユーティリティがやっているのは、
あくまで「文字列の形がそれっぽいかどうか」の判定だけ。
“正しいことの保証”ではなく、“明らかにおかしいものを弾くフィルタ” だという感覚を持っておいてください。
例題:入力フォームでの電話番号チェック
ユーザー登録フォームで、電話番号を入力してもらう場面を考えます。
サーバ側では、こんな感じで使えます。
String phone = request.getParameter("phone");
if (phone == null || phone.isBlank()) {
errors.add("電話番号を入力してください。");
} else if (!PhoneNumberValidator.isDomesticPhoneNumber(phone)) {
errors.add("電話番号の形式が正しくありません。");
}
Javaここでのポイントは、メールのときと同じく、
「未入力」と「形式が変」を分けてメッセージを出す
チェックは必ずサーバ側でも行う
という二つです。
フロント側でのリアルタイムバリデーション(JavaScript)も便利ですが、
最終的にDBに入る前に、サーバ側で必ずチェックする、
という意識を持っておくと、システムの信頼性が一気に上がります。
まとめ:電話番号チェックユーティリティで身につけたい感覚
電話番号チェックは、「完璧な判定」を目指すものではなく、
「明らかにおかしいものを早めにはじくためのフィルタ」 です。
まずは、
数字とハイフンだけ許可する
ハイフンを抜いたときに 10〜11 桁
先頭は 0 から始まる
といったシンプルなルールを、isDomesticPhoneNumber のようなメソッドに落とし込んでみてください。
そのうえで、必要に応じて
ハイフンの位置まで見る正規表現版を用意する
国際番号付き用の別メソッドを用意する
といった形で、用途ごとにメソッドを分けていく。
もしあなたのコードのどこかに、
電話番号を「文字列としてそのまま受け取って、そのままDBに入れている」箇所があれば、
そこを題材にして、ここで作った PhoneNumberValidator.isDomesticPhoneNumber を一度挟んでみてください。
それだけで、「明らかにおかしい電話番号がシステムに入り込む」リスクを、かなり減らすことができます。
