Java Tips | 文字列処理:電話番号チェック

Java Java
スポンサーリンク

電話番号チェックは「“電話っぽい数字”だけを通す」ための技

業務システムでは、電話番号を入力してもらう場面が本当に多いです。
問い合わせフォーム、会員登録、配送先情報、緊急連絡先…。

ここでチェックをしないと、
「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 を一度挟んでみてください。

それだけで、「明らかにおかしい電話番号がシステムに入り込む」リスクを、かなり減らすことができます。

タイトルとURLをコピーしました