在 Java 中,String 类的正则方法(如 matches())适用于简单场景,但复杂的字符串匹配(如多次查找、条件匹配)需依赖 java.util.regex.Pattern 和 Matcher 类。这两个类是正则表达式的核心实现,支持灵活的子串查找、分组提取、条件匹配等高级功能。本文结合实例,解析其用法与实战场景。
一、Pattern 与 Matcher 核心作用
- Pattern:正则表达式的编译表示,用于预编译正则规则(线程安全),通过
Pattern.compile(String regex)创建实例,避免重复编译提升效率。 - Matcher:文本匹配器,根据 Pattern 规则在目标字符串中查找匹配子串,支持多次查找、分组提取等操作(非线程安全)。
核心流程:
- 用
Pattern.compile(regex)编译正则表达式,得到 Pattern 对象; - 用
pattern.matcher(text)创建 Matcher 对象,关联目标字符串; - 用 Matcher 的
find()查找匹配子串,group()获取匹配结果。
二、基础用法:从文本中提取子串
1. 本地文本爬取
从字符串中提取符合规则的子串(如提取所有 “Java + 版本号” 格式的文本):
String text = "Java自从95年问世以来,企业中常用Java8和Java11,下一个长期支持版本是Java17...";
// 1. 编译正则:匹配"Java"后跟0-2位数字
Pattern pattern = Pattern.compile("Java\\d{0,2}");
// 2. 创建匹配器,关联目标文本
Matcher matcher = pattern.matcher(text);
// 3. 循环查找所有匹配子串
while (matcher.find()) {
System.out.println(matcher.group()); // 输出:Java、Java8、Java11、Java17
}
find():从当前位置向后查找下一个匹配子串,找到返回true,并记录子串的起始 / 结束索引;group():返回当前匹配的子串(需在find()返回true后调用)。
2. 网络文本爬取
通过 URL 读取网页内容,并用正则提取特定信息(如爬取网页中的身份证号):
// 1. 连接网页
URL url = new URL("https://www.mytxly.com/sfz/");
URLConnection conn = url.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// 2. 编译正则:匹配18位身份证号(首位非0,后17位为数字)
Pattern pattern = Pattern.compile("[1-9]\\d{17}");
// 3. 逐行读取并匹配
String line;
while ((line = br.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
System.out.println(matcher.group()); // 输出网页中所有身份证号
}
}
br.close();
优势:相比 String 方法,可批量处理大文本(如网页内容),支持逐行匹配和多次查找。
三、高级特性:条件匹配与分组控制
1. 非捕获分组与预查
通过特殊分组构造,实现 “有条件的匹配”(如只匹配特定后缀的子串,或排除特定情况):
| 构造语法 | 作用 | 示例场景 |
|---|---|---|
(?:正则) | 非捕获分组(仅分组,不记录组号) | 匹配 “Java8、Java11” 但不单独提取版本号 |
(?=正则) | 正向预查(匹配 “前面部分”,要求后面符合正则) | 只提取后面跟 8/11/17 的 “Java” |
(?!正则) | 反向预查(匹配 “前面部分”,要求后面不符合正则) | 提取后面不跟 8/11/17 的 “Java” |
示例:条件爬取 “Java” 相关文本
String text = "Java8、Java11、java17、Java20...";
// 需求1:只提取后面跟8/11/17的“Java”(不区分大小写)
Pattern p1 = Pattern.compile("((?i)Java)(?=8|11|17)");
// 需求2:提取“Java+8/11/17”完整文本(非捕获分组)
Pattern p2 = Pattern.compile("((?i)Java)(?:8|11|17)");
// 需求3:提取后面不跟8/11/17的“Java”
Pattern p3 = Pattern.compile("((?i)Java)(?!8|11|17)");
// 匹配并输出结果
Matcher m1 = p1.matcher(text);
while (m1.find()) System.out.println(m1.group()); // 输出:Java、Java、java
Matcher m2 = p2.matcher(text);
while (m2.find()) System.out.println(m2.group()); // 输出:Java8、Java11、java17
Matcher m3 = p3.matcher(text);
while (m3.find()) System.out.println(m3.group()); // 输出:Java(对应Java20中的Java)
(?i):修饰符,表示匹配时忽略大小写。
2. 贪婪与非贪婪匹配
正则默认是 “贪婪匹配”(尽可能多匹配字符),通过在数量词后加 ? 可切换为 “非贪婪匹配”(尽可能少匹配):
String text = "abbbbbbbbbbbbaaaaaaaaa...";
// 贪婪匹配:"ab+" 匹配"ab"后尽可能多的b
Pattern greedyPattern = Pattern.compile("ab+");
Matcher greedyMatcher = greedyPattern.matcher(text);
while (greedyMatcher.find()) {
System.out.println(greedyMatcher.group()); // 输出:abbbbbbbbbbbb(最长匹配)
}
// 非贪婪匹配:"ab+?" 匹配"ab"后尽可能少的b(仅1个b)
Pattern lazyPattern = Pattern.compile("ab+?");
Matcher lazyMatcher = lazyPattern.matcher(text);
while (lazyMatcher.find()) {
System.out.println(lazyMatcher.group()); // 输出:ab(最短匹配)
}
适用场景:
- 贪婪匹配:需要提取最长符合规则的子串(如提取完整标签
<div>...</div>); - 非贪婪匹配:需要提取最短符合规则的子串(如提取多个短标签
<a>...</a>)。
四、总结:Pattern 与 Matcher 的优势
相比 String 类的正则方法,Pattern 和 Matcher 更适合复杂场景:
- 批量查找:通过
find()循环提取所有匹配子串(而非单次匹配); - 高级匹配:支持条件预查、分组控制、贪婪 / 非贪婪模式,满足复杂规则;
- 性能优化:
Pattern预编译正则表达式,避免重复编译,适合高频匹配场景(如爬虫、日志解析)。
掌握这两个类,可轻松实现文本提取、数据清洗、网络爬虫等功能,是处理复杂字符串的核心工具。
2188

被折叠的 条评论
为什么被折叠?



