郵箱驗證是表單校驗的高頻需求。雖然 RFC 5322 定義的完整郵箱語法極其複雜,但實際業務中通常只需覆蓋 99% 的常見格式。本文提供生產可用的正則模式,解析常見錯誤寫法,並給出單元測試建議。
// 生產環境推薦:平衡嚴謹度與通用性
private static final String EMAIL_REGEX =
"^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@" +
"(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}$";
| 片段 | 含義 |
|---|---|
^[a-zA-Z0-9_+&*-]+ | 本地部分開頭:字母、數字、底線、+、&、*、-,至少一個 |
(?:\\.[a-zA-Z0-9_+&*-]+)* | 允許點號分隔的多段,但不能連續點號、不能以點號開頭/結尾 |
@ | 必須包含單一 @ 符號 |
(?:[a-zA-Z0-9-]+\\.)+ | 域名部分:子域名由字母數字連字符組成,以點號結尾,至少一段 |
[a-zA-Z]{2,}$ | 頂級域名:至少兩個字母 |
import java.util.regex.Pattern;
public class EmailValidator {
private static final Pattern EMAIL_PATTERN = Pattern.compile(
"^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@" +
"(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}$"
);
/** 驗證郵箱格式 */
public static boolean isValid(String email) {
if (email == null || email.length() > 254) return false; // RFC 5321 限制
return EMAIL_PATTERN.matcher(email).matches();
}
/** 驗證並標準化(轉小寫、去除首尾空白) */
public static String normalize(String email) {
if (email == null) return null;
String trimmed = email.trim().toLowerCase();
return isValid(trimmed) ? trimmed : null;
}
// 單元測試範例
public static void main(String[] args) {
String[] valid = {
"user@example.com", "USER@EXAMPLE.COM", "user.name@sub.domain.com",
"user+tag@example.co.uk", "user_name@domain.io", "u@x.org"
};
String[] invalid = {
"user@", "@domain.com", "user@.com", "user@domain.",
"user@domain.c", "user..name@domain.com", ".user@domain.com",
"user@domain..com", "user@-domain.com", "user@domain-.com",
"user name@domain.com", "user@domain.com ", " user@domain.com",
"", "a@b.c", "a@b" // 過短或格式錯誤
};
System.out.println("=== 應通過 ===");
for (String e : valid)
System.out.printf("%-30s %s%n", e, isValid(e) ? "✓" : "✗ FAIL");
System.out.println("\n=== 應拒絕 ===");
for (String e : invalid)
System.out.printf("%-30s %s%n", e, isValid(e) ? "✗ FALSE PASS" : "✓");
}
}
| 錯誤模式 | 問題 | 修正 |
|---|---|---|
\\w+@\\w+\\.\\w+ | 不支援點號、+、-、多級域名、頂級域名超過 3 字符 | 使用上述推薦模式 |
.+@.+\\..+ | 過於寬鬆,允許空格、多個 @、無效字符 | 限制字符集、錨定開頭結尾 |
[^@]+@[^@]+\\.[^@]+ | 允許連續點號、開頭結尾點號、特殊字符 | 分段限制字符集 |
原文範例:\\p{Alpha}\\w{2,15}[@][a-z0-9]{3,}[.]\\p{Lower}{2,} | 強制首字母為字母、本地部分最長 16 字符、域名最短 3 字符、不支援大寫域名、不支援多級域名 | 移除過度限制,支援國際化域名 |
支援 Unicode 本地部分與域名(如 用戶@例子.中國),需使用 \\p{L} 匹配 Unicode 字母,但 Java 正則對 Unicode 屬性支援有限。實際專案建議:
完整 RFC 5322 正則超過 6000 字符,難以維護,且允許註釋、引號字符串、IP 域名等極少見格式。業務上「發送驗證信」才是真正的驗證。
Pattern 為靜態常量,避免重複編譯。Matcher 物件複用(ThreadLocal)。