1. 概述
在 Java 中处理正则表达式时,我们通常需要在一个字符序列中查找特定的 Pattern。为此,Java 正则表达式 API 提供了 Matcher 类,用于将正则表达式与目标文本进行匹配。
✅ 核心要点:实际开发中,我们最常使用的 Matcher 方法就两个:
find()
matches()
本文通过几个简单但典型的例子,带你彻底搞清这两个方法的差异,避免踩坑。
2. find() 方法:查找任意位置的匹配
find()
的作用很简单:在目标字符串中查找是否存在与正则匹配的子串,只要有一处匹配就返回 true。
⚠️ 关键特性:
- 不要求整个字符串完全匹配
- 可以多次调用,每次找到下一个匹配项(类似迭代器)
- 常用于提取多个匹配结果
举个例子:我们要从字符串 "goodbye 2019 and welcome 2020"
中提取所有四位数。
使用正则 \d\d\d\d
:
@Test
public void whenFindFourDigitWorks_thenCorrect() {
Pattern stringPattern = Pattern.compile("\\d\\d\\d\\d");
Matcher m = stringPattern.matcher("goodbye 2019 and welcome 2020");
assertTrue(m.find()); // 找到第一个:2019
assertEquals(8, m.start());
assertEquals("2019", m.group());
assertEquals(12, m.end());
assertTrue(m.find()); // 找到第二个:2020
assertEquals(25, m.start());
assertEquals("2020", m.group());
assertEquals(29, m.end());
assertFalse(m.find()); // 没有了,返回 false
}
📌 输出说明:
start()
:匹配子串的起始索引(含)end()
:匹配子串结束后的下一个位置(不含)group()
:实际匹配到的内容
✅ 两次 find()
调用分别捕获了 2019
和 2020
,第三次返回 false
,说明已遍历完毕。
3. find(int) 方法:从指定位置开始查找
find(int start)
是 find()
的重载版本,允许你指定搜索的起始位置。
✅ 使用场景:跳过前面的内容,只关心某位置之后的匹配。
继续上面的例子,如果我们只想找索引 20 之后的四位数:
@Test
public void givenStartIndex_whenFindFourDigitWorks_thenCorrect() {
Pattern stringPattern = Pattern.compile("\\d\\d\\d\\d");
Matcher m = stringPattern.matcher("goodbye 2019 and welcome 2020");
assertTrue(m.find(20)); // 从索引 20 开始找
assertEquals(25, m.start());
assertEquals("2020", m.group());
assertEquals(29, m.end());
}
结果只匹配到 2020
,因为 2019
出现在索引 8,早于 20,被跳过了。
💡 提示:这个方法在解析日志、分段处理文本时特别有用,避免重复匹配。
4. matches() 方法:全字符串匹配
matches()
和 find()
完全不同:它要求整个字符串必须完全匹配正则表达式。
❌ 常见误区:以为 matches()
是“有没有匹配”,其实是“是不是完全匹配”。
看例子:
@Test
public void whenMatchFourDigitWorks_thenFail() {
Pattern stringPattern = Pattern.compile("\\d\\d\\d\\d");
Matcher m = stringPattern.matcher("goodbye 2019 and welcome 2020");
assertFalse(m.matches()); // 返回 false
}
原因:\d\d\d\d
只能匹配 4 个数字,而整个字符串远不止这些字符。
但如果输入本身就是 2019
,那就完全匹配了:
@Test
public void whenMatchFourDigitWorks_thenCorrect() {
Pattern stringPattern = Pattern.compile("\\d\\d\\d\d");
Matcher m = stringPattern.matcher("2019");
assertTrue(m.matches()); // 成功
assertEquals(0, m.start());
assertEquals("2019", m.group());
assertEquals(4, m.end());
}
✅ 注意:matches()
是幂等的,无论调用多少次,只要输入不变,结果都一样。而 find()
多次调用会移动指针,结果可能不同。
5. matcher() 与 Pattern.matches() 的区别
这里容易混淆,我们来澄清一下:
方法 | 类型 | 行为 |
---|---|---|
pattern.matcher(input) |
实例方法 | 返回 Matcher 对象,可多次操作(如 find() 、matches() ) |
Pattern.matches(regex, input) |
静态方法 | 编译正则 + 全字符串匹配,一步到位 |
简单粗暴理解:
matcher()
:“字符串里有没有这个模式?”Pattern.matches()
:“字符串是不是这个模式?”
示例对比:
@Test
public void whenUsingMatcher_thenReturnTrue() {
Pattern pattern = Pattern.compile("\\d\\d\\d\\d");
Matcher matcher = pattern.matcher("goodbye 2019 and welcome 2020");
assertTrue(matcher.find()); // ✅ 找得到 2019
}
@Test
public void whenUsingMatches_thenReturnFalse() {
String REGEX = "\\d\\d\\d\\d";
String STRING_INPUT = "goodbye 2019 and welcome 2020";
assertFalse(Pattern.matches(REGEX, STRING_INPUT)); // ❌ 不是全数字
}
📌 实际建议:
- 需要提取内容、多轮匹配 → 用
matcher() + find()
- 只做一次校验(如手机号、邮箱格式)→ 可直接用
Pattern.matches()
6. 总结
方法 | 匹配范围 | 是否可迭代 | 典型用途 |
---|---|---|---|
find() |
任意子串 | ✅ 可多次调用 | 提取多个匹配项 |
find(int) |
从指定位置开始 | ✅ | 跳过前段内容 |
matches() |
整个字符串 | ❌ 幂等 | 格式校验 |
Pattern.matches() |
静态全匹配 | ❌ | 简单一次性校验 |
✅ 核心口诀:
find
找得到,matches
全符合。
掌握这两者的区别,能让你在处理文本解析、日志提取、输入校验等场景时少走弯路。代码已上传至 GitHub 仓库:https://github.com/dev-example/java-regex-demo。