Java 正则进阶:Pattern 与 Matcher 详解及实战爬取

在 Java 中,String 类的正则方法(如 matches())适用于简单场景,但复杂的字符串匹配(如多次查找、条件匹配)需依赖 java.util.regex.Pattern 和 Matcher 类。这两个类是正则表达式的核心实现,支持灵活的子串查找、分组提取、条件匹配等高级功能。本文结合实例,解析其用法与实战场景。

一、Pattern 与 Matcher 核心作用

  • Pattern:正则表达式的编译表示,用于预编译正则规则(线程安全),通过 Pattern.compile(String regex) 创建实例,避免重复编译提升效率。
  • Matcher:文本匹配器,根据 Pattern 规则在目标字符串中查找匹配子串,支持多次查找、分组提取等操作(非线程安全)。

核心流程

  1. 用 Pattern.compile(regex) 编译正则表达式,得到 Pattern 对象;
  2. 用 pattern.matcher(text) 创建 Matcher 对象,关联目标字符串;
  3. 用 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 更适合复杂场景:

  1. 批量查找:通过 find() 循环提取所有匹配子串(而非单次匹配);
  2. 高级匹配:支持条件预查、分组控制、贪婪 / 非贪婪模式,满足复杂规则;
  3. 性能优化Pattern 预编译正则表达式,避免重复编译,适合高频匹配场景(如爬虫、日志解析)。

掌握这两个类,可轻松实现文本提取、数据清洗、网络爬虫等功能,是处理复杂字符串的核心工具。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值