1. 概述
在实际开发中,经常会遇到需要校验字符串格式的场景,比如邮箱、身份证、手机号等。本文聚焦于如何使用 Java 正则表达式(Regular Expressions)来验证常见的手机号码格式。
虽然手机号格式因国家而异,但我们可以从简单到复杂,逐步构建出能覆盖多种格式的正则表达式。✅ 掌握这些技巧,能帮你少踩不少坑。
2. 验证手机号的正则表达式
2.1 十位纯数字号码
最简单的场景:验证一个字符串是否为 恰好10位数字,不包含任何其他字符。
@Test
public void whenMatchesTenDigitsNumber_thenCorrect() {
Pattern pattern = Pattern.compile("^\\d{10}$");
Matcher matcher = pattern.matcher("2055550125");
assertTrue(matcher.matches());
}
📌 解析:
^
表示字符串开始\\d{10}
表示连续10个数字$
表示字符串结束
✅ 匹配示例:2055550125
⚠️ 注意:这种写法非常严格,不允许空格、横线等分隔符。
2.2 支持空格、点号或横线分隔
现实中用户输入更自由,比如 202 555 0125
、202.555.0125
、202-555-0125
。我们可以通过添加可选分隔符来增强匹配能力。
@Test
public void whenMatchesTenDigitsNumberWhitespacesDotHyphen_thenCorrect() {
Pattern pattern = Pattern.compile("^(\\d{3}[- .]?){2}\\d{4}$");
Matcher matcher = pattern.matcher("202 555 0125");
assertTrue(matcher.matches());
}
📌 解析:
\\d{3}[- .]?
:匹配3位数字 + 可选的一个空格、.
或-
{2}
:前面的结构重复两次(即前6位)\\d{4}
:最后4位数字
✅ 支持格式:
2055550125
202 555 0125
202.555.0125
202-555-0125
💡 小技巧:[- .]
中的空格代表真正的空格字符,顺序无关紧要。
2.3 支持括号格式
有些用户习惯把区号用括号括起来,例如 (202) 555-0125
。我们需要让正则支持这种写法。
@Test
public void whenMatchesTenDigitsNumberParenthesis_thenCorrect() {
Pattern pattern = Pattern.compile("^((\\(\\d{3}\\))|\\d{3})[- .]?\\d{3}[- .]?\\d{4}$");
Matcher matcher = pattern.matcher("(202) 555-0125");
assertTrue(matcher.matches());
}
📌 解析:
(\\(\\d{3}\\))
:匹配(202)
这种带括号的3位数字(注意括号要转义)|
:或操作\\d{3}
:或者直接3位数字- 外层
(...)
将两者组合成一个整体
✅ 支持格式:
(202)5550125
(202) 555-0125
(202)-555-0125
- 也兼容前面所有无括号格式
⚠️ 踩坑提醒:正则中 (
和 )
是特殊字符,必须写成 \\(
和 \\)
才能匹配字面意义的括号。
2.4 支持国际区号前缀
全球化的应用中,手机号可能带有国际前缀,如 +1
、+86
等。通常前缀以 +
开头,后跟1-3位国家代码。
@Test
public void whenMatchesTenDigitsNumberPrefix_thenCorrect() {
Pattern pattern = Pattern.compile("^(\\+\\d{1,3}( )?)?((\\(\\d{3}\\))|\\d{3})[- .]?\\d{3}[- .]?\\d{4}$");
Matcher matcher = pattern.matcher("+111 (202) 555-0125");
assertTrue(matcher.matches());
}
📌 解析:
(\\+\\d{1,3}( )?)?
:整个国际前缀是可选的(末尾的?
)\\+
:匹配+
符号\\d{1,3}
:1到3位数字( )?
:可选的空格
- 后半部分沿用之前的逻辑
✅ 支持格式:
+1 202 555 0125
+86(10)12345678
+111 202.555.0125
💡 实际项目中,可根据业务需要调整国家代码位数限制。
3. 组合多个正则表达式
有时候单一正则无法覆盖所有合法格式。比如除了北美格式,你还想支持欧洲常见的分组方式(如 +44 20 7946 0958
),这时可以 将多个正则用 |
(或)连接,形成一个“组合正则”。
示例场景
我们想同时支持以下三种模式:
- 北美标准格式(含括号、分隔符等)
- 纯数字三三分组:
+111 123 456 789
- 欧洲风格分组:
+111 123 45 67 89
@Test
public void whenMatchesPhoneNumber_thenCorrect() {
String patterns
= "^(\\+\\d{1,3}( )?)?((\\(\\d{3}\\))|\\d{3})[- .]?\\d{3}[- .]?\\d{4}$"
+ "|^(\\+\\d{1,3}( )?)?(\\d{3}[ ]?){2}\\d{3}$"
+ "|^(\\+\\d{1,3}( )?)?(\\d{3}[ ]?)(\\d{2}[ ]?){2}\\d{2}$";
String[] validPhoneNumbers
= {"2055550125","202 555 0125", "(202) 555-0125", "+111 (202) 555-0125",
"636 856 789", "+111 636 856 789", "636 85 67 89", "+111 636 85 67 89"};
Pattern pattern = Pattern.compile(patterns);
for(String phoneNumber : validPhoneNumbers) {
Matcher matcher = pattern.matcher(phoneNumber);
assertTrue(matcher.matches());
}
}
📌 关键点:
- 每个子表达式独立编写,逻辑清晰
- 用
|
连接,表示“任意一个匹配即成功” - 注意转义和字符串拼接时的引号处理
✅ 优势:
- 灵活扩展:新增格式只需加一个
|...
- 易于维护:每个分支职责单一
❌ 缺点:
- 正则变长后可读性下降
- 性能略低于单一优化正则(但通常可忽略)
💡 建议:如果业务中手机号格式较复杂,推荐先写多个小正则测试通过后,再合并使用。
4. 总结
本文通过由浅入深的方式,展示了如何使用 Java 正则表达式验证手机号码:
- 从最简单的10位纯数字开始
- 逐步增加对分隔符、括号、国际前缀的支持
- 最后通过
|
操作符组合多个正则,覆盖更广的格式
📌 实际项目建议:
- 不要过度追求“万能正则”,容易变得难以维护
- 结合业务需求选择合适复杂度的校验规则
- 对于高要求场景,可考虑使用专门的库如 Google's libphonenumber
示例代码已上传至 GitHub:https://github.com/example/java-regex-phone-validator