正则表达式 = 字符串的筛选模板——把「一个字符一个字符地 if」压缩成一行规则,校验、切割、替换三种活一肩挑。
6.8 正则表达式
1. 为什么需要正则?
5.5 给 Person.setAge 做过安检:age >= 0 && age <= 150。现在换个活——校验手机号:
// 老写法:逐条 if
if (s.length() == 11
&& s.charAt(0) == '1'
&& s.charAt(1) >= '3' && s.charAt(1) <= '9'
&& 全是数字……) { ... }
正则写法:
boolean ok = s.matches("1[3-9]\\d{9}"); // 一行顶十行
正则的本质:用一个「模板字符串」描述一类字符串的长相。模板由四类零件拼成。
2. 四大零件(语法速查)
| 类别 | 写法 | 含义 | 示例 |
|---|---|---|---|
| ① 精确字符 | abc @ - |
原样逐位匹配 | abc 配 "abc" |
| ② 字符集合 | [abc] [a-z] [^0-9] |
方括号里选一个;^ 开头取反 |
1[3-9] 配 13~19 |
| ③ 量词 | * + ? {n} {n,} {n,m} |
前一个东西重复几次 | \d{9} 九位数字 |
| ④ 预定义 | \d \w \s . |
数字 / 单词字符 / 空白 / 任意一个 | \d+ 一串数字 |
量词对照:
| 量词 | 次数 | 记法 |
|---|---|---|
* |
0 次或多次 | 随便 |
+ |
1 次或多次 | 至少来一个 |
? |
0 次或 1 次 | 有没有都行 |
{n} |
恰好 n 次 | 定死的 |
{n,m} |
n 到 m 次 | 弹性区间 |
正则里"数字"写 \d,但 Java 字符串里 \ 本身是转义符——所以源代码必须写 "\\d"(两个斜杠到正则手里剩一个)。
同理:正则匹配「点」写 \.,源代码写 "\\."(点不转义会变成"任意字符")。
口诀:Java 字符串里的正则,斜杠一律翻倍。
3. Java 三种使用姿势(String 自带,不用 import)
姿势一:matches —— 整串校验
String phone = "13812345678";
System.out.println(phone.matches("1[3-9]\\d{9}")); // true
String bad = "12812345678"; // 第二位 2 不在 [3-9]
System.out.println(bad.matches("1[3-9]\\d{9}")); // false
注意:matches 要求整串完全匹配——多一位少一位、位置不对都是 false,不是"包含就匹配"。
姿势二:split —— 正则切割
6.2 用 split 只能按单一字符切;分隔符其实是个正则,能力瞬间升级:
"a,b;c d".split("[,; ]") // [a, b, c, d] —— 三种分隔符一次切
"a b c".split("\\s+") // [a, b, c] —— 连续空白一刀切干净
"a,b".split(",") // 老用法照样行
姿势三:replaceAll —— 正则替换
"138 1234 5678".replaceAll("\\s", "") // 13812345678 —— 去掉所有空格
"abc123def456".replaceAll("\\d", "*") // abc***def*** —— 所有数字打码
"我a爱bC".replaceAll("[a-zA-Z]", "") // 我爱 —— 按类删除
6.2 的 replace 只能换死字符串;replaceAll 换的是一类。
4. 综合实战:三个经典模板
public class Demo08 {
public static void main(String[] args) {
// ① 手机号校验(5.5 的 setPhone 安检直接升级)
System.out.println("13912345678".matches("1[3-9]\\d{9}")); // true
// ② 邮箱校验(简化版)
System.out.println("[email protected]".matches("\\w+@\\w+\\.[a-z]{2,}")); // true
// ③ 成绩单拆分:split + parseInt(6.2 毕业题的正则版)
String line = "语文=95,数学=88,英语=92";
for (String subject : line.split(",")) {
String[] kv = subject.split("=");
System.out.println(kv[0] + ":" + kv[1]);
}
}
}
\w+@\w+\.[a-z]{2,} 中的点必须转义——不转义的 . 是"任意字符",a@bXcom 也能混过去,校验就白做了。
5. 常见误区
编译直接报错——Java 字符串里 \d 不是合法转义(Invalid escape sequence)。源代码里斜杠一律翻倍。
"abc13812345678".matches("1[3-9]\\d{9}") 是 false——matches 从头到尾整串比对。想"找一段"要用 Pattern/Matcher 的 find(),后面再学。
正则语法错误(比如 "[abc" 少半边括号)编译不拦,运行时抛 PatternSyntaxException——运行时异常,5.2 收容所那派。模板复杂时先在在线正则工具里试,再搬进代码。
. 在正则里是"任意字符"。校验类模板里遇到字面的点(域名、文件扩展名)必须 \\.。
正则强在格式校验,不管业务规则:能验"像不像手机号",验不了"这号注册过没"。格式归正则、业务归 if,别混。
6. 动手试一试(30 秒实验)
新建 Demo08:
实验 1:"a1b2c3".matches("[a-z]\\d") 结果是 false——因为是 6 个字符不是 3 个;改成 "[a-z]\\d[a-z]\\d[a-z]\\d" 试试,再改 ([a-z]\\d){3} 一遍过——体会量词+分组的威力。
实验 2:"a,b;c d".split("[,; ]") 和 .split("\\s+") 各打一遍,数数组长度——体会字符集合与 \s+ 的区别。
实验 3:"abc123".replaceAll(".", "*") ——参数只写一个 .(Java 字符串里点不用转义),结果整串变 ******——亲眼见正则里"点=任意字符";再改成 "\\." 试 [email protected]".replaceAll("\\.", "#"),只有真正的点被替换——对照体会转义。
实验 4:写 "[abc" 作为 matches 参数,看运行时抛 PatternSyntaxException——确认它编译不拦。
7. 自测一下
数字、单词字符(字母数字下划线)、空白。正则本身是 \d,Java 字符串里 \ 是转义符,必须写 \\ 才能传给正则引擎一个 \——两层皮。
模板逐位咬合:1(第一位)+ [3-9](第二位)+ \d{9}(后九位)= 11 位全定义。少一位则整串匹配失败返回 false——matches 是全串校验。
["a", "b", "c"]——\s+(预定义+量词)按连续空白切,|(或)把逗号并进来,字符集合也行:"[,\\s]+"。
不转义的 . 配任意字符,a@bxc om 变形串(如 a@bx1com)也能通过——校验形同虚设。凡字面的点必须 \\.。
两步:先 replaceAll("\\s", "") 去空格得 13812345678;再 replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")——分组保留前 3 后 4,中间四位换星号。(分组 $1/$2 混脸熟即可,后面细学)
8. 下一步学什么
- ☑ 6.9 本章小结 ✅ —— 已完成:点此复习 —— 第六章完结 🎉:八节串成工具箱,两条暗线收网,毕业大题订单录入一题串四件套。
相关笔记
- 第六章 Java API/6.7 日期时间格式化类 ← 上一篇:同为"模板匹配字符串"的思想——模板逐位咬合,正则是它的通用版
- 第六章 Java API/6.2 字符串类:split / replace 的老用法,本节升级成正则版
- 第五章 异常/5.2 运行时异常与编译时异常:PatternSyntaxException = 运行时异常(编译不拦)
- 第五章 异常/5.3 异常处理及语法:setter 安检的 if 写法,本节正则一行顶十行