String.matches()不能直接用^d{1,3}.d{1,3}.d{1,3}.d{1,3}$,因它不校验0–255数值范围且允许前导零;正确方式是分段精确匹配或用split+parseInt逐段验证。

String.matches() 为什么不能直接用 ^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$
这个正则看起来能匹配四段数字加点的结构,但 String.matches() 默认做**全字符串匹配**(隐式加了 ^ 和 $),所以你不用再手动写。真正的问题是:它无法阻止像 "999.999.999.999" 这种明显越界的值——\d{1,3} 只管位数,不管数值范围。而且它也接受 "01.02.03.04" 这类带前导零的非法 IP 段(IPv4 规范中,每个段必须是 0–255 的十进制整数,且不能有前导零,除非就是 "0")。
正确写法:用四组独立的数值范围正则组合
必须把每一段的合法取值精确表达出来。核心是拆解 0–255:
-
0单独匹配 -
1–9开头的 1 位或 2 位数:[1-9]\d? -
100–199:1\d{2} -
200–249:2[0-4]\d -
250–255:25[0-5]
合并成一段: (?:0|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}),注意 \d{1,2} 必须放在最后,否则会误吞 12(本该走 [1-9]\d? 分支);但更稳妥的是显式写 [1-9]\d? 替代 \d{1,2},避免歧义。
完整正则(已转义用于 Java 字符串):
立即学习“Java免费学习笔记(深入)”;
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
"^(?:0|1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d?)\.(?:0|1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d?)\.(?:0|1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d?)\.(?:0|1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d?)$"
使用时直接传给 matches():
"192.168.1.1".matches("^(?:0|1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d?)\.(?:0|1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d?)\.(?:0|1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d?)\.(?:0|1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d?)$") // true
常见错误和坑
这些写法看似简洁,实则不可靠:
- 用
\d+或\d{1,3}—— 放行999、00、012 - 漏掉非捕获分组
(?:...),导致matches()因分组数量问题行为异常(虽不影响布尔结果,但语义不清) - 忘记对点号
.转义,写成.而不是\.,结果匹配任意字符 - 在字符串里只写单反斜杠:
"^\d{1,3}.\d{1,3}..."→ 实际传给正则引擎的是^d{1,3}.d{1,3}...,完全失效 - 没加锚点
^和$—— 虽然matches()自带,但若后续改用find()就会出错,建议统一显式写出,增强可移植性
更推荐的做法:先 split 再逐段 parseInt 验证
正则写起来绕、难读、难维护。对 IP 校验这种逻辑清晰的场景,代码反而更可靠:
public static boolean isValidIp(String ip) {
if (ip == null) return false;
String[] parts = ip.split("\.", -1); // -1 保留末尾空段
if (parts.length != 4) return false;
for (String part : parts) {
if (part.isEmpty()) return false;
if (part.length() > 1 && part.charAt(0) == '0') return false; // 前导零
try {
int num = Integer.parseInt(part);
if (num < 0 || num > 255) return false;
} catch (NumberFormatException e) {
return false;
}
}
return true;
}
它天然处理了前导零、空段、非数字等边界,逻辑直白,调试友好。只有在纯正则受限环境(如配置文件规则引擎)才硬上复杂正则。
正则里最易被忽略的其实是“前导零”和“空段”——split("\.") 默认丢弃空段,"192.168.1." 会变成长度为 3 的数组,而 split("\.", -1) 才能暴露末尾空段问题。

















