C# Tips | 文字列処理:電話番号検証

C# C#
スポンサーリンク

はじめに 「電話番号検証」は“ぐちゃっとした入力を、安全な数字列に整える”仕事

電話番号入力って、郵便番号以上に“ゆらぎ”が激しいです。

03-1234-5678
09012345678
03−1234−5678
(+81) 90-1234-5678

人によって書き方がバラバラなのに、
システム側は「ちゃんとした電話番号として扱いたい」「検索や帳票では統一した形で出したい」。

ここで本当に欲しいのは、

「人間のゆらぎを受け止めつつ、内部では“数字だけの電話番号”として扱えるユーティリティ」

です。

ここでは、初心者向けに、

  • 電話番号検証の考え方(“完全正解”ではなく“現実解”を目指す)
  • 数字だけに正規化するユーティリティ
  • 桁数や先頭の番号で“それっぽいか”をチェックする
  • ハイフン付き表示用に再フォーマットする

までを、例題付きでかみ砕いて説明していきます。


電話番号検証の方針を決める 「何をOKとするか?」

日本の電話番号をざっくり整理する

厳密にやろうとすると総務省レベルの話になるので、
ここでは“実務でよく使う範囲”に絞ります。

ざっくり、こんな感じです。

  • 固定電話(例)03-1234-5678045-123-4567 など
  • 携帯電話(例)090-1234-5678080-xxxx-xxxx
  • フリーダイヤルなど(例)0120-xxx-xxx

実務でよくやるのは、

「数字だけにして、10桁 or 11桁ならOKとみなす(日本国内想定)」

という現実解です。

  • 固定電話 → だいたい 10 桁
  • 携帯電話 → だいたい 11 桁

ここをベースにして、細かいルール(先頭が 0 かどうか、など)を足していきます。


ステップ1:数字だけに正規化する

まずは「数字以外は全部ノイズ」とみなす

どんな書き方をされていても、
最終的には「数字だけの電話番号」にしたいので、
最初の一歩は「数字だけを抜き出す」ことです。

using System;
using System.Linq;

public static class PhoneNumberNormalizer
{
    public static string ExtractDigits(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return string.Empty;
        }

        // 全角数字も考えるなら、先に全角→半角変換を挟むのもアリ
        return new string(value.Where(char.IsDigit).ToArray());
    }
}
C#

やっていることはシンプルです。

  • null や空白だけなら空文字を返す
  • 1文字ずつ見て、char.IsDigit が true のものだけを拾う
  • new string(...ToArray()) で数字だけの文字列にする

動作例でイメージを固める

Console.WriteLine(PhoneNumberNormalizer.ExtractDigits("03-1234-5678"));        // 0312345678
Console.WriteLine(PhoneNumberNormalizer.ExtractDigits("090-1234-5678"));       // 09012345678
Console.WriteLine(PhoneNumberNormalizer.ExtractDigits("(+81) 90-1234-5678"));  // 819012345678
Console.WriteLine(PhoneNumberNormalizer.ExtractDigits("abc"));                 // 空文字
Console.WriteLine(PhoneNumberNormalizer.ExtractDigits(null));                  // 空文字
C#

ここで重要なのは、

「数字以外は全部捨てる」

という割り切りです。
このあと、「桁数」や「先頭の数字」で“それっぽいか”を判定していきます。


ステップ2:「日本の電話番号としてそれっぽいか」を判定する

10桁 or 11桁かどうかを見る

まずは、数字だけにした結果が「10桁 or 11桁かどうか」を見ます。

public static class PhoneNumberValidator
{
    public static bool IsLikelyJapanesePhoneNumber(string? value)
    {
        string digits = PhoneNumberNormalizer.ExtractDigits(value);

        // 10桁 or 11桁のみ許可
        if (digits.Length == 10 || digits.Length == 11)
        {
            // 先頭が 0 でないものは日本の電話番号としては怪しいので弾く
            return digits[0] == '0';
        }

        return false;
    }
}
C#

ここでは、

  • 数字だけにして 10 桁 or 11 桁なら候補
  • さらに「先頭が 0 かどうか」を見る(日本の電話番号はだいたい 0 から始まる)

という“ざっくりした現実解”を採用しています。

動作例

Console.WriteLine(PhoneNumberValidator.IsLikelyJapanesePhoneNumber("03-1234-5678"));   // true(10桁、先頭0)
Console.WriteLine(PhoneNumberValidator.IsLikelyJapanesePhoneNumber("090-1234-5678"));  // true(11桁、先頭0)
Console.WriteLine(PhoneNumberValidator.IsLikelyJapanesePhoneNumber("123-456-7890"));   // false(先頭1)
Console.WriteLine(PhoneNumberValidator.IsLikelyJapanesePhoneNumber("090-1234-567"));   // false(桁数不足)
Console.WriteLine(PhoneNumberValidator.IsLikelyJapanesePhoneNumber("abc"));            // false
C#

この時点で、「明らかに電話番号ではないもの」はかなり弾けます。


ステップ3:正規化+検証を一度に行うユーティリティ

Try パターンで「正規化済みの数字列」を返す

実務では、

「入力が妥当なら“正規化済みの数字列”を返してほしい」

ことが多いので、
TryParse 風のメソッドにしておくと使いやすくなります。

public static class PhoneNumberUtil
{
    public static bool TryNormalizeJapanesePhoneNumber(string? value, out string normalizedDigits)
    {
        normalizedDigits = string.Empty;

        string digits = PhoneNumberNormalizer.ExtractDigits(value);

        // 10桁 or 11桁のみ許可
        if (digits.Length != 10 && digits.Length != 11)
        {
            return false;
        }

        // 先頭が 0 でないものは弾く
        if (digits[0] != '0')
        {
            return false;
        }

        normalizedDigits = digits;
        return true;
    }
}
C#

動作例

void Test(string? input)
{
    if (PhoneNumberUtil.TryNormalizeJapanesePhoneNumber(input, out var normalized))
    {
        Console.WriteLine($"{input} -> OK: {normalized}");
    }
    else
    {
        Console.WriteLine($"{input} -> NG");
    }
}

Test("03-1234-5678");          // OK: 0312345678
Test("090-1234-5678");         // OK: 09012345678
Test("03−1234−5678"); // OK: 0312345678(※全角対応を足せばこうできる)
Test("123-456-7890");          // NG
Test("090-1234-567");          // NG
C#

ここまでで、

  • 「妥当な電話番号かどうか」を判定しつつ
  • 「内部で使いやすい“数字だけの電話番号”」を取り出す

というユーティリティができました。


全角数字・全角ハイフン・国番号をどう扱うか

全角数字・全角ハイフンを半角に寄せる

郵便番号のときと同じように、
全角数字・全角ハイフンを半角に寄せてから ExtractDigits する、という設計もよく使います。

public static class ZenkakuToHankakuUtil
{
    public static string ToHalfWidthDigitsAndHyphen(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            return value;
        }

        var chars = value.ToCharArray();

        for (int i = 0; i < chars.Length; i++)
        {
            char c = chars[i];

            if (c >= '0' && c <= '9')
            {
                chars[i] = (char)('0' + (c - '0'));
            }
            else if (c == 'ー' || c == '−' || c == '―' || c == '-')
            {
                chars[i] = '-';
            }
        }

        return new string(chars);
    }
}
C#

これを ExtractDigits の前に挟めば、
"03−1234−5678" のような入力もきれいに扱えます。

国番号付き(+81)の扱い

(+81) 90-1234-5678 のような国番号付きの電話番号をどう扱うかは、要件次第です。

  • 「国内向けシステムなので、国番号付きは想定しない」
    → そのまま弾いてしまってもよい
  • 「国番号付きも受け取りたいが、内部では国内形式にしたい」
    81 から始まる場合に 0 に変換するなどのロジックを足す

例えば、819012345678(+81 90 1234 5678)を 09012345678 に寄せるなら、
「先頭が 81 で、続く桁数がそれっぽい場合に 0 を付け直す」といった処理を追加します。
これは少し込み入るので、「必要になったら足す」くらいのスタンスでOKです。


ステップ4:表示用に「ハイフン付き」に整形する

内部は数字だけ、表示はハイフン付き

内部では「数字だけ」にしておくと扱いやすいですが、
画面や帳票では「03-1234-5678」のようにハイフン付きで出したいことが多いです。

ここでは、ざっくりとした整形ルールを決めてしまいます。

  • 10桁 → 固定電話として XX-XXXX-XXXX 形式にする
  • 11桁 → 携帯電話として XXX-XXXX-XXXX 形式にする
public static class PhoneNumberFormatter
{
    public static string FormatWithHyphen(string digits)
    {
        if (digits is null)
        {
            return string.Empty;
        }

        if (digits.Length == 10)
        {
            // 固定電話っぽい: 2-4-4 に切る(03-1234-5678 など)
            return $"{digits.Substring(0, 2)}-{digits.Substring(2, 4)}-{digits.Substring(6, 4)}";
        }

        if (digits.Length == 11)
        {
            // 携帯っぽい: 3-4-4 に切る(090-1234-5678 など)
            return $"{digits.Substring(0, 3)}-{digits.Substring(3, 4)}-{digits.Substring(7, 4)}";
        }

        // それ以外はそのまま返す(想定外)
        return digits;
    }
}
C#

動作例

Console.WriteLine(PhoneNumberFormatter.FormatWithHyphen("0312345678"));   // 03-1234-5678
Console.WriteLine(PhoneNumberFormatter.FormatWithHyphen("09012345678"));  // 090-1234-5678
Console.WriteLine(PhoneNumberFormatter.FormatWithHyphen("123"));          // 123(想定外なのでそのまま)
C#

実際には「市外局番の長さ」によって切り方を変えることもできますが、
まずは「2-4-4」「3-4-4」のようなシンプルルールから始めるのが現実的です。


設計のポイント 「検証」「正規化」「表示」を分ける

電話番号ユーティリティを設計するときに大事なのは、
役割をきちんと分けることです。

  • 「数字だけにする」 → ExtractDigits
  • 「日本の電話番号としてそれっぽいか判定」 → IsLikelyJapanesePhoneNumber / TryNormalizeJapanesePhoneNumber
  • 「表示用にハイフン付きに整形」 → FormatWithHyphen

こう分けておくと、

  • 入力チェックでは TryNormalizeJapanesePhoneNumber を使う
  • DB には normalizedDigits(数字だけ)を保存する
  • 画面表示では FormatWithHyphen で見やすくする

という流れが自然に組めます。

「どこまでゆらぎを許すか」「国番号をどう扱うか」などは、
このユーティリティの中に“ルールとして埋め込む”イメージです。


まとめ 「電話番号検証ユーティリティ」は“人間のバラバラ入力を、機械に優しい形にそろえるフィルタ”

電話番号検証は、「正規表現で形だけ見る」だけでは足りません。
実務で本当に効いてくるのは、

  • 数字だけに正規化してから考える
  • 桁数(10 or 11)と先頭の数字(0)で“日本の電話番号っぽさ”を判定する
  • 全角数字・全角ハイフンも現実的に扱う
  • 内部は数字だけ、表示はハイフン付きに整形する
  • 「検証」「正規化」「表示」の役割を分けてユーティリティ化する

というあたりです。

ここまで押さえれば、「なんとなく電話番号っぽいか見ている」段階から一歩進んで、
“人間のバラバラな入力を、機械に優しい形にそろえる”
業務・実務で本当に使える C# の電話番号検証ユーティリティを、自分の手で設計・実装できるようになっていきます。

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