正则表达式
约 931 字大约 3 分钟
布欧-Lewyon
2026-05-16
正则表达式(Regular Expression)是一种描述字符串模式的语法,常用于校验格式、提取信息和替换文本。Java 通过 java.util.regex 包提供支持。
Pattern 与 Matcher
核心是两个类:Pattern 编译正则字符串为模式对象,Matcher 在目标字符串上执行匹配。
Pattern pattern = Pattern.compile("\\d+"); // 匹配一个或多个数字
Matcher matcher = pattern.matcher("abc123def456");
while (matcher.find()) {
System.out.println(matcher.group()); // 123, 456
}Pattern是不可变且线程安全的,可缓存复用。Matcher有内部状态,非线程安全,每次匹配应创建新实例。
常用语法速查
| 模式 | 含义 | 示例匹配 |
|---|---|---|
. | 任意字符(除换行) | a、1 |
\d | 数字 [0-9] | 5 |
\w | 单词字符 [a-zA-Z0-9_] | A、_ |
\s | 空白字符(空格、Tab、换行) | 、\t |
^ | 行首 | |
$ | 行尾 | |
* | 前一个元素出现 0~n 次 | |
+ | 前一个元素出现 1~n 次 | |
? | 前一个元素出现 0 或 1 次 | |
{n,m} | 前一个元素出现 n~m 次 | \d{3,5} |
[abc] | 字符集:a、b、c 之一 | [aeiou] 匹配元音 |
[^abc] | 排除字符集 | |
( ) | 分组,捕获匹配内容 |
在 Java 字符串中反斜线需要转义,\d 要写成 "\\d",\. 要写成 "\\."。
分组
括号 () 除了控制优先级,还能捕获匹配的子串:
Pattern pattern = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher matcher = pattern.matcher("2026-05-16");
if (matcher.matches()) {
System.out.println(matcher.group(0)); // 2026-05-16(完整匹配)
System.out.println(matcher.group(1)); // 2026
System.out.println(matcher.group(2)); // 05
System.out.println(matcher.group(3)); // 16
}命名分组(Java 7+)
Pattern pattern = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})");
Matcher matcher = pattern.matcher("2026-05");
if (matcher.matches()) {
System.out.println(matcher.group("year")); // 2026
}String 上的正则方法
String 本身提供几个使用正则的方法,日常编码更常用:
String s = "Hello, Java 2026!";
s.matches("\\w+"); // false(需要完全匹配)
s.matches("Hello.*"); // true
s.split("\\s+"); // ["Hello,", "Java", "2026!"]
s.split("[,\\s!]+"); // ["Hello", "Java", "2026"]
s.replaceAll("\\d+", "YEAR"); // "Hello, Java YEAR!"
s.replaceFirst("\\w+", "Hi"); // "Hi, Java 2026!"matches()要求整个字符串匹配模式,等价于在正则前后隐式加了^和$。replaceAll和replaceFirst中可使用$1引用分组。
// 交换姓名顺序 "Last, First" → "First Last"
"Liu, Yuan".replaceAll("(\\w+),\\s*(\\w+)", "$2 $1"); // "Yuan Liu"常见模式示例
// Email 校验(简化版)
Pattern EMAIL = Pattern.compile("^[\\w.-]+@[\\w.-]+\\.\\w{2,}$");
EMAIL.matcher("user@example.com").matches(); // true
// 手机号(中国大陆,简化版)
Pattern PHONE = Pattern.compile("^1[3-9]\\d{9}$");
// 提取 HTML 标签中的文本
Pattern TAG = Pattern.compile("<\\w+>(.*?)</\\w+>");
// 注意:实际解析 HTML 应使用专门的解析器(如 Jsoup),正则不适合嵌套结构性能注意
- 正则编译(
Pattern.compile)开销不小,重用Pattern而非每次编译。 - 贪婪量词(
*、+)默认尽可能多匹配,遇到复杂模式可能导致灾难性回溯。能用*?、+?(懒惰模式)或++(占有模式)时优先使用。 - 简单字符串操作(
startsWith、contains)比正则快得多,仅在需要模式匹配时才用正则。
小结
- 正则核心:
Pattern.compile(regex)→matcher(input)→find()/matches()/group()。 - 分组用
()捕获子串,命名分组通过(?<name>)定义、group("name")读取。 String.split、replaceAll、matches是最常用的正则入口。- 复杂嵌套结构(HTML、JSON)不要用正则,使用专用解析器。
- 易错:Java 字符串中反斜线需写两次(
"\\d")。matches()是完全匹配,find()是部分查找,两者行为不同容易混淆。 - 思考任务:写一个方法
boolean isValidPhone(String s),用正则校验中国大陆手机号(11 位、1 开头、第二位 3-9)。再写一个从日志行2026-05-16 14:30:00 [INFO] 用户 1001 登录中提取日期、级别和用户 ID 的正则。
上一节:String 与日期时间
下一节:序列化
