Java Tips | 文字列処理:メール形式チェック

Java Java
スポンサーリンク

メール形式チェックは「“それっぽい文字列”を早めにはじく」ための技

業務システムでは、ユーザーにメールアドレスを入力してもらう場面が山ほどあります。
ログインID、問い合わせフォーム、会員登録、通知先設定…。

ここでまったくチェックをしないと、
「あきらかにメールじゃない文字列」がDBに入り、
後でメール送信に失敗したり、バッチがコケたりします。

そこで使うのが「メール形式チェック」です。
これは「この文字列が“メールアドレスとしてあり得る形かどうか”をざっくり判定する」ためのユーティリティです。
完璧な判定ではなく、「明らかにおかしいものを早めにはじく」くらいのイメージで捉えるとちょうどいいです。


まずは「メールアドレスのざっくりルール」を知る

完璧を目指さず“実務で困らないライン”を決める

メールアドレスの正式な仕様(RFC)は、正直かなり複雑です。
"name"@example.com のようにダブルクォートが使えたり、
+._ など、いろんな記号が許されていたりします。

でも、実務でそこまで完璧に対応しようとすると、
正規表現が怪物みたいになって、誰も読めなくなります。

だから現場では、だいたい次のような「ざっくりルール」でチェックすることが多いです。

「@ が 1つだけ含まれている」
「@ の前後に 1文字以上ある」
「ドメイン部分に . が含まれていて、最後の . の後に 2文字以上ある」

これくらいでも、「明らかにおかしいもの」はかなり弾けます。
そして、これを正規表現に落とし込んだものが、よくある“実務用メール形式チェック”です。


シンプルなメール形式チェックユーティリティ

正規表現で「それっぽい形か」を判定する

まずは、実務でよく使うレベルのシンプルなチェックを書いてみます。

import java.util.regex.Pattern;

public final class EmailValidator {

    private EmailValidator() {}

    // かなりシンプルな「それっぽいメールアドレス」判定用パターン
    private static final Pattern SIMPLE_EMAIL =
            Pattern.compile("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$");

    public static boolean isValid(String email) {
        if (email == null) {
            return false;
        }
        return SIMPLE_EMAIL.matcher(email).matches();
    }
}
Java

使い方はこうなります。

System.out.println(EmailValidator.isValid("taro@example.com"));      // true
System.out.println(EmailValidator.isValid("taro.suzuki@example.co.jp")); // true
System.out.println(EmailValidator.isValid("taro@localhost"));        // false(このルールではNG)
System.out.println(EmailValidator.isValid("taro@@example.com"));     // false
System.out.println(EmailValidator.isValid("taro example@example.com")); // false(空白があるのでNG)
System.out.println(EmailValidator.isValid("taro@example"));          // false
Java

ここで重要なのは、正規表現の意味をちゃんと理解しておくことです。

^$ は「文字列の先頭」と「文字列の末尾」を表します。
これで「全体がこのパターンに一致していること」を保証します。

[^@\\s]+ は「@ と空白以外の文字が 1文字以上」という意味です。
これが @ の前と後にそれぞれ置かれているので、
「@ の前後に 1文字以上ある」「空白は含まない」という条件になります。

\\. は「ドットそのもの」です。
正規表現では . は「任意の1文字」という意味なので、
「ドットという文字」を表したいときは \\. とエスケープします。

つまりこのパターンは、
「空白なし」「@ は1つ」「@ の前後に1文字以上」「ドメイン部分に少なくとも1つドットがある」
という、かなり素直な条件を表しています。


もう少しだけ厳しめにするバージョン

ローカル部とドメイン部に“使える文字”を制限する

さっきのパターンは「@ と空白以外なら何でもOK」でした。
もう少しだけ厳しくして、「よくある文字だけ許可する」こともできます。

例えば、ローカル部(@ の前)は英数字と ._%+-
ドメイン部(@ の後)は英数字と .-
最後のドットの後は英字2文字以上、というルールにしてみます。

import java.util.regex.Pattern;

public final class EmailValidator {

    private EmailValidator() {}

    // よく見る「実務用」メールアドレスチェック
    private static final Pattern COMMON_EMAIL =
            Pattern.compile("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");

    public static boolean isCommonValid(String email) {
        if (email == null) {
            return false;
        }
        return COMMON_EMAIL.matcher(email).matches();
    }
}
Java

使い方はこうです。

System.out.println(EmailValidator.isCommonValid("taro@example.com"));        // true
System.out.println(EmailValidator.isCommonValid("taro.suzuki+test@example.co.jp")); // true
System.out.println(EmailValidator.isCommonValid("taro@sub-domain.example.com"));    // true

System.out.println(EmailValidator.isCommonValid("taro@localhost"));          // false
System.out.println(EmailValidator.isCommonValid("taro@例え.テスト"));          // false(日本語ドメインはこのルールではNG)
System.out.println(EmailValidator.isCommonValid("taro@@example.com"));       // false
System.out.println(EmailValidator.isCommonValid("taro example@example.com"));// false
Java

ここで深掘りしたいポイントは二つです。

一つ目は、「この正規表現は“仕様的に完全ではない”が、“現場でよく使われる妥協ライン”」だということです。
日本語ドメインや、RFC的にはOKな特殊なアドレスは弾いてしまいますが、
その代わり、パターンがまだギリギリ読めるレベルに収まっています。

二つ目は、「用途によってパターンを使い分ける」という発想です。
社内システムで、ほぼ英数字だけのメールアドレスしか使わないなら、このくらいで十分です。
一方で、グローバル向けサービスで、あらゆる形式のメールアドレスを受け入れたいなら、
もっと柔軟なライブラリやバリデーションを検討する必要があります。


「形式チェック」と「実在チェック」は別物だと理解する

メール形式チェックユーティリティがやっているのは、
あくまで「文字列の形がそれっぽいかどうか」の判定だけです。

taro@example.com が「本当に存在するアドレスかどうか」は、
形式チェックだけでは絶対に分かりません。

実在チェックをしたいなら、
実際にメールを送って、ユーザーに確認リンクを踏んでもらう、
いわゆる「メールアドレス認証」の仕組みが必要になります。

ここで大事なのは、
「形式チェックは“明らかにおかしいものを弾くフィルタ”であって、
“正しいことの保証”ではない」
という感覚を持っておくことです。


例題:入力フォームでのメール形式チェック

ユーザー登録フォームで、メールアドレスを入力してもらう場面を考えます。

サーバ側では、こんな感じで使えます。

String email = request.getParameter("email");

if (email == null || email.isBlank()) {
    errors.add("メールアドレスを入力してください。");
} else if (!EmailValidator.isCommonValid(email)) {
    errors.add("メールアドレスの形式が正しくありません。");
}
Java

ここでのポイントは二つです。

一つ目は、「null や空文字のチェック」と「形式チェック」を分けていることです。
「未入力」と「形式が変」は、ユーザーに伝えるメッセージも違うので、
条件も別々に書いたほうが親切です。

二つ目は、「形式チェックは“サーバ側で必ずやる”」ということです。
フロント側でJavaScriptバリデーションをしていても、
悪意あるユーザーはそれを無効化してリクエストを送ってきます。
サーバ側のチェックが最終防衛ラインだと考えてください。


まとめ:メール形式チェックユーティリティで身につけたい感覚

メール形式チェックは、「完璧な判定」を目指すものではなく、
「明らかにおかしいものを早めにはじくためのフィルタ」です。

まずは、^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$ のようなシンプルなパターンから始めて、
「@ が1つ」「前後に文字がある」「ドメインにドットがある」といった最低限のルールをコードに落とし込んでみてください。

そのうえで、必要に応じて
「使える文字を絞ったパターン(COMMON_EMAIL)」にするのか、
「ライブラリ(Apache Commons Validator など)に任せる」のかを選んでいく。

もしあなたのコードのどこかに、
メールアドレスを「文字列としてそのまま受け取って、そのままDBに入れている」箇所があれば、
そこを題材にして、ここで作った EmailValidator.isValidisCommonValid を一度挟んでみてください。

それだけで、「明らかにおかしいメールアドレスがシステムに入り込む」リスクを、かなり減らすことができます。

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