正则表达式:文本处理的瑞士军刀
你一定遇到过这些场景:
- 用户注册时,要验证手机号格式是否正确
- 日志文件里,要提取所有 ERROR 开头的行
- 表单提交时,要把 HTML 标签全部过滤掉
- 爬虫抓取时,要从网页源码里提取邮箱和 URL
每次遇到这种"从一堆文本里找特定模式"的问题,你是不是都想写一堆 if-else 和 substring?
那你就需要正则表达式了。
正则是什么?
正则表达式(Regular Expression)是描述字符串模式的一种语法。你可以把它理解为一把瑞士军刀——不是单一功能,而是多种文本处理能力的集合。
比如,你想匹配"以数字开头、以字母结尾的字符串",用自然语言描述很简单,但用代码怎么写?
java
// 没有正则的日子
if (str.length() > 0 && Character.isDigit(str.charAt(0))
&& Character.isLetter(str.charAt(str.length() - 1))) {
// 匹配成功
}
// 有正则的日子
if (str.matches("^\\d.*[a-zA-Z]$")) {
// 匹配成功
}差距一目了然。
快速上手:5 分钟入门
第一个正则程序
java
import java.util.regex.*;
public class FirstRegex {
public static void main(String[] args) {
String text = "我的订单号是 20260315001,明天记得发货";
// \\d 代表任意数字,+ 代表一个或多个
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(text);
// find() 会找到所有匹配的子串
while (m.find()) {
System.out.println("找到: " + m.group());
}
}
}输出:
找到: 20260315001解释一下:
\\d不是反斜杠 d,而是正则语法,代表"数字"- 在 Java 字符串里需要写成
\\d(因为单个\需要转义) +是量词,表示前面的元素出现一次或多次find()每次调用都会找下一个匹配,直到没有为止
常用快捷字符
这是你最需要记住的几个"快捷方式":
| 写法 | 含义 | 等价于 |
|---|---|---|
\d | 数字 | [0-9] |
\D | 非数字 | [^0-9] |
\w | 单词字符 | [a-zA-Z0-9_] |
\W | 非单词字符 | [^a-zA-Z0-9_] |
\s | 空白字符 | [ \t\n\x0B\f\r] |
. | 任意字符 | 除了换行符 |
记住技巧:大写字母是大写的反义——\d 是数字,\D 就是"非数字"。
踩坑预警:点号
初学者最常犯的错误就是忘记转义点号。
java
public class DotTrap {
public static void main(String[] args) {
// 场景:检查文件名是否是 .txt 结尾
// ❌ 错误写法
String badPattern = ".txt";
System.out.println("anythingtxt".matches(badPattern)); // true!因为 . 匹配任意字符
// ✅ 正确写法:加点号转义
String goodPattern = "\\.txt";
System.out.println("anything.txt".matches(goodPattern)); // true
System.out.println("anythingtxt".matches(goodPattern)); // false
}
}. 在正则里是"任意字符"的代名词,不是字面意思的点号。要匹配真正的点号,必须写成 \\.。
实用场景:从日志里捞数据
看一个真实场景:解析日志文件中的关键信息。
java
import java.util.regex.*;
public class LogParser {
public static void main(String[] args) {
String log = "2026-03-22 10:30:15 ERROR [OrderService] 订单处理失败,订单号: ORD-20260322-001";
// 日志正则分解
String logPattern = "(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}) (\\w+) .*订单号: (.+)";
Pattern p = Pattern.compile(logPattern);
Matcher m = p.matcher(log);
if (m.matches()) {
System.out.println("日期: " + m.group(1)); // 2026-03-22
System.out.println("时间: " + m.group(2)); // 10:30:15
System.out.println("级别: " + m.group(3)); // ERROR
System.out.println("订单号: " + m.group(4)); // ORD-20260322-001
}
}
}分组捕获:() 里的内容会被捕获,通过 m.group(1)、m.group(2) 提取。
三种常用操作:验证、提取、替换
1. 验证:matches()
检查整个字符串是否符合模式:
java
public class ValidationDemo {
public static void main(String[] args) {
// 验证手机号
String phone = "13812345678";
boolean valid = phone.matches("^1[3-9]\\d{9}$");
System.out.println("手机号有效: " + valid); // true
// ❌ 常见的错误:忘记 ^ 和 $
String badPhone = "x13812345678x";
boolean invalid = badPhone.matches("1[3-9]\\d{9}"); // true!因为没有锚定
System.out.println("误判为有效: " + invalid);
}
}教训:验证时一定要加 ^ 和 $,否则只检查"是否包含"而非"是否完全匹配"。
2. 提取:find()
从文本中找出匹配的片段:
java
public class ExtractDemo {
public static void main(String[] args) {
String html = "邮箱: test@example.com,电话: admin@test.org";
Pattern p = Pattern.compile("\\w+@\\w+\\.\\w+");
Matcher m = p.matcher(html);
while (m.find()) {
System.out.println("提取: " + m.group());
}
}
}输出:
提取: test@example.com
提取: admin@test.org3. 替换:replaceAll()
把匹配的部分替换成别的内容:
java
public class ReplaceDemo {
public static void main(String[] args) {
String text = "原价 299 元,双十一特价 199 元,错过等一年!";
// 把所有数字替换成 #
String result1 = text.replaceAll("\\d+", "#");
System.out.println(result1); // 原价 # 元,双十一特价 # 元,错过等一年!
// 只替换第一个数字
String result2 = text.replaceFirst("\\d+", "XXX");
System.out.println(result2); // 原价 XXX 元,双十一特价 199 元,错过等一年!
}
}一张图记住核心语法
位置锚点: ^ 开头 $ 结尾 \b 单词边界
字符类: \d 数字 \w 字母 \s 空白 . 任意
量词: * 0+ + 1+ ? 0-1 {n} {n,} {n,m}
分组: () 捕获 (?:) 非捕获总结
正则表达式是处理文本的利器,掌握它只需要记住几点:
- 快捷字符:
\d是数字,\w是字母数字下划线,\s是空白 - 量词:
+是一个或多个,*是零个或多个,?是零个或一个 - 转义:真正匹配
.要写\\.,匹配\要写\\\\ - 锚点:验证时别忘加
^和$ - 分组:用
()捕获,$1、$2引用
学会了这些,你就能写出:
- 邮箱验证:
^\\w+@\\w+\\.\\w+$ - 手机号:
^1[3-9]\\d{9}$ - 提取数字:
\\d+
下一节我们深入讲语法细节:正则表达式语法
