深入理解正则表达式中的"整个匹配"
1. 什么是"整个匹配"?
整个匹配(Whole Match)指的是正则表达式模式在整个输入字符串中成功匹配的最长连续子字符串。它对应着 Matcher.group(0) 或 Matcher.group()。
基本概念
String text = "The price is $25.99 for the item";
String regex = "\\$\\d+\\.\\d{2}"; // 匹配货币格式
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
if (matcher.find()) {
System.out.println("整个匹配: '" + matcher.group(0) + "'");
System.out.println("起始位置: " + matcher.start());
System.out.println("结束位置: " + matcher.end());
}
输出:
整个匹配: '$25.99'
起始位置: 12
结束位置: 18
2. 整个匹配 vs 捕获组
public class WholeMatchVsGroups {
public static void main(String[] args) {
String text = "2023-12-25";
String regex = "(\\d{4})-(\\d{2})-(\\d{2})";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
if (matcher.matches()) {
System.out.println("=== 匹配结构分析 ===");
System.out.println("整个匹配 (group 0): '" + matcher.group(0) + "'");
System.out.println("捕获组 1 (年份): '" + matcher.group(1) + "'");
System.out.println("捕获组 2 (月份): '" + matcher.group(2) + "'");
System.out.println("捕获组 3 (日期): '" + matcher.group(3) + "'");
System.out.println("\n=== 位置信息 ===");
System.out.println("整个匹配范围: " + matcher.start() + "-" + matcher.end());
System.out.println("组1范围: " + matcher.start(1) + "-" + matcher.end(1));
System.out.println("组2范围: " + matcher.start(2) + "-" + matcher.end(2));
System.out.println("组3范围: " + matcher.start(3) + "-" + matcher.end(3));
}
}
}
输出:
=== 匹配结构分析 ===
整个匹配 (group 0): '2023-12-25'
捕获组 1 (年份): '2023'
捕获组 2 (月份): '12'
捕获组 3 (日期): '25'
=== 位置信息 ===
整个匹配范围: 0-10
组1范围: 0-4
组2范围: 5-7
组3范围: 8-10
3. 整个匹配的边界特性
贪婪匹配的影响
public class GreedyMatching {
public static void main(String[] args) {
String text = "abc123def456ghi";
// 贪婪匹配 - 匹配尽可能多的字符
String greedyRegex = "[a-z]+\\d+";
Pattern greedyPattern = Pattern.compile(greedyRegex);
Matcher greedyMatcher = greedyPattern.matcher(text);
// 懒惰匹配 - 匹配尽可能少的字符
String lazyRegex = "[a-z]+?\\d+";
Pattern lazyPattern = Pattern.compile(lazyRegex);
Matcher lazyMatcher = lazyPattern.matcher(text);
System.out.println("原始文本: " + text);
System.out.println("\n贪婪匹配 ([a-z]+\\d+):");
while (greedyMatcher.find()) {
System.out.println(" 整个匹配: '" + greedyMatcher.group() + "'");
}
System.out.println("\n懒惰匹配 ([a-z]+?\\d+):");
while (lazyMatcher.find()) {
System.out.println(" 整个匹配: '" + lazyMatcher.group() + "'");
}
}
}
输出:
原始文本: abc123def456ghi
贪婪匹配 ([a-z]+\\d+):
整个匹配: 'abc123'
整个匹配: 'def456'
懒惰匹配 ([a-z]+?\\d+):
整个匹配: 'abc123'
整个匹配: 'def456'
4. 锚点与整个匹配
public class AnchorsAndWholeMatch {
public static void main(String[] args) {
String[] texts = {"hello world", "hello", "world hello"};
String regex = "^hello"; // 必须以hello开头
Pattern pattern = Pattern.compile(regex);
for (String text : texts) {
Matcher matcher = pattern.matcher(text);
System.out.println("\n文本: '" + text + "'");
System.out.println("模式: '^hello'");
if (matcher.find()) {
System.out.println("✓ 找到匹配: '" + matcher.group() + "'");
System.out.println(" 匹配位置: " + matcher.start() + "-" + matcher.end());
} else {
System.out.println("✗ 未找到匹配");
}
}
// 使用 matches() 方法 - 必须匹配整个字符串
System.out.println("\n=== 使用 matches() 方法 ===");
String fullMatchRegex = "hello.*";
Pattern fullPattern = Pattern.compile(fullMatchRegex);
for (String text : texts) {
Matcher matcher = fullPattern.matcher(text);
System.out.println("文本: '" + text + "'");
System.out.println("完全匹配? " + matcher.matches());
}
}
}
5. 复杂模式中的整个匹配
public class ComplexPatternWholeMatch {
public static void main(String[] args) {
String html = "<div class=\"container\"><p>Hello World</p></div>";
// 匹配HTML标签及其内容
String regex = "<(\\w+)([^>]*)>([^<]*)</\\1>";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(html);
System.out.println("HTML: " + html);
System.out.println("\n匹配分析:");
while (matcher.find()) {
System.out.println("\n=== 找到匹配 ===");
System.out.println("整个匹配: '" + matcher.group(0) + "'");
System.out.println("标签名 (组1): '" + matcher.group(1) + "'");
System.out.println("属性 (组2): '" + matcher.group(2) + "'");
System.out.println("内容 (组3): '" + matcher.group(3) + "'");
System.out.println("\n位置信息:");
System.out.println("整个匹配: " + matcher.start() + "-" + matcher.end());
System.out.println("标签名: " + matcher.start(1) + "-" + matcher.end(1));
System.out.println("属性: " + matcher.start(2) + "-" + matcher.end(2));
System.out.println("内容: " + matcher.start(3) + "-" + matcher.end(3));
}
}
}
6. 整个匹配在实际应用中的重要性
文本提取和替换
public class TextExtraction {
public static void main(String[] args) {
String document = """
联系人: 张三, 电话: 138-0011-2233, 邮箱: zhangsan@example.com
联系人: 李四, 电话: 139-4455-6677, 邮箱: lisi@company.com
联系人: 王五, 电话: 137-8899-0011, 邮箱: wangwu@test.org
""";
// 提取每个人的完整联系信息
String personRegex = "联系人:\\s*(\\S+),\\s*电话:\\s*(\\S+),\\s*邮箱:\\s*(\\S+)";
Pattern pattern = Pattern.compile(personRegex);
Matcher matcher = pattern.matcher(document);
System.out.println("提取的联系人信息:");
while (matcher.find()) {
System.out.println("\n整个匹配的记录: '" + matcher.group(0) + "'");
System.out.println("姓名: " + matcher.group(1));
System.out.println("电话: " + matcher.group(2));
System.out.println("邮箱: " + matcher.group(3));
}
// 使用整个匹配进行替换
String anonymized = document.replaceAll(personRegex, "[个人信息已隐藏]");
System.out.println("\n脱敏后文档:\n" + anonymized);
}
}
7. 理解匹配边界的深层含义
public class MatchBoundaries {
public static void main(String[] args) {
String text = "cat category catalog cataclysm";
String[] patterns = {
"cat", // 简单匹配
"\\bcat\\b", // 单词边界
"cat\\w*", // 以cat开头的单词
"\\bcat\\w*" // 单词边界 + 以cat开头
};
for (String patternStr : patterns) {
System.out.println("\n模式: " + patternStr);
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println(" 匹配: '" + matcher.group() +
"' 位置: " + matcher.start() + "-" + matcher.end());
}
}
}
}
输出:
模式: cat
匹配: 'cat' 位置: 0-3
匹配: 'cat' 位置: 4-7
匹配: 'cat' 位置: 12-15
匹配: 'cat' 位置: 20-23
模式: \bcat\b
匹配: 'cat' 位置: 0-3
模式: cat\w*
匹配: 'cat' 位置: 0-3
匹配: 'category' 位置: 4-12
匹配: 'catalog' 位置: 13-20
匹配: 'cataclysm' 位置: 21-30
模式: \bcat\w*
匹配: 'cat' 位置: 0-3
匹配: 'category' 位置: 4-12
匹配: 'catalog' 位置: 13-20
匹配: 'cataclysm' 位置: 21-30
8. 性能考虑:整个匹配的开销
public class PerformanceConsideration {
public static void main(String[] args) {
String longText = "abc123def456ghi789".repeat(1000);
// 测量不同方法的性能
long startTime, endTime;
// 方法1: 只检查是否存在匹配
startTime = System.nanoTime();
Pattern pattern1 = Pattern.compile("\\d{3}");
Matcher matcher1 = pattern1.matcher(longText);
boolean hasMatch = matcher1.find();
endTime = System.nanoTime();
System.out.println("只检查匹配存在: " + (endTime - startTime) + " ns");
// 方法2: 获取整个匹配内容
startTime = System.nanoTime();
Pattern pattern2 = Pattern.compile("\\d{3}");
Matcher matcher2 = pattern2.matcher(longText);
if (matcher2.find()) {
String wholeMatch = matcher2.group(0); // 这里产生额外开销
}
endTime = System.nanoTime();
System.out.println("获取整个匹配: " + (endTime - startTime) + " ns");
// 方法3: 获取所有捕获组
startTime = System.nanoTime();
Pattern pattern3 = Pattern.compile("(\\d)(\\d)(\\d)");
Matcher matcher3 = pattern3.matcher(longText);
if (matcher3.find()) {
String group1 = matcher3.group(1);
String group2 = matcher3.group(2);
String group3 = matcher3.group(3);
}
endTime = System.nanoTime();
System.out.println("获取所有捕获组: " + (endTime - startTime) + " ns");
}
}
关键理解要点
- 整个匹配是基础: 所有捕获组都是整个匹配的子集
- 位置关系: 整个匹配的位置范围包含所有捕获组的位置范围
- 性能影响: 获取整个匹配比只检查匹配存在有额外开销
- 边界重要: 锚点和边界符会显著影响整个匹配的结果
- 贪婪策略: 量词的贪婪/懒惰影响整个匹配的长度
- 实际应用: 整个匹配在文本提取、替换和验证中起核心作用
理解"整个匹配"的概念对于有效使用正则表达式至关重要,它是所有匹配操作的起点和基础。
Java正则中的整个匹配解析
941

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



