一、QQ验证正则表达式分析
在深入探讨URL解析之前,我们先回顾一个经典的QQ号码验证正则表达式,它展示了分组的基本应用:
// QQ号码验证
// 要求:是1-9开头的一个(5位数-10位数)比如:12389
String regStr = "^[1-9]\\d{4,9}$";
这个表达式相对简单,它没有使用分组来捕获特定部分,而是作为一个整体进行匹配验证,结果显而易见:
二、URL解析正则表达式深度剖析
现在,让我们重点分析你提供的URL解析代码,这里面的分组机制更加复杂且有趣。
import java.util.regex.*;
public class Main {
public static void main(String[] args) {
String content = "https://www.bilibili.***/video/BV1Eq4y1E79W?spm_id_from=333.788.player.switch&vd_source=dec3407385f13b51755d19fd7f30ac63&p=17";
String regStr = "^((http|https)://)([\\w]+\\.)+[\\w]+(\\/[\\w?=.&/]+)?$";
Pattern pattern = Pattern.***pile(regStr);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
System.out.println("完整匹配: " + matcher.group(0));
System.out.println("group(1): " + matcher.group(1)); // 协议部分
System.out.println("group(2): " + matcher.group(2)); // http或https
System.out.println("group(3): " + matcher.group(3)); // 域名部分
System.out.println("group(4): " + matcher.group(4)); // 路径参数部分
} else {
System.out.println("匹配失败");
}
}
}
值得一提的是,这上面的网址,就是博主最喜欢的老师的正则表达式的教学视频。
运行结果如下:
2.2 正则表达式分组结构分解
1.让我们详细解析这个正则表达式^((http|https)://)([\\w]+\\.)+[\\w]+(\\/[\\w?=.&/]+)?$的分组结构:
2.分组编号group(1)对应((http|https)://),负责捕获完整的协议头部分。当匹配URL时,这个分组会提取出类似https://这样的完整协议标识符。
3.分组编号group(2)对应(http|https),嵌套在第一个分组内部,专门用于捕获具体的协议类型。这个分组会精确地提取出http或https这样的协议名称。
4.分组编号group(3)对应([\\w]+\\.)是一个需要特别关注的分组,因为它后面跟着量词+。这个分组的设计目的是匹配一个或多个由单词字符和点号组成的序列,例如www.、bilibili.这样的域名组成部分。
5.分组编号group(4)对应(\\/[\\w?=.&/]+),负责捕获路径和查询参数部分。这个分组会匹配URL中斜杠后面的所有路径信息和参数列表。
2.3 关键发现:分组匹配的特殊行为
分组group(3)的行为特别值得深入分析。由于后面跟着量词+,该分组会尝试多次匹配,但最终只保留最后一次成功匹配的结果。
以URLhttps://www.bilibili.***/video...为例,匹配过程会经历几个关键步骤:
首先匹配到www.,此时分组3临时保存了www.。由于量词+的存在,引擎会尝试继续匹配,随后成功匹配到bilibili.。这时,分组3中之前保存的www.会被覆盖为bilibili.。引擎继续尝试匹配***,但***后面没有点号,不符合[\w]+\.的模式,因此匹配失败。最终,group(3)返回的是最后一次成功匹配的内容,即bilibili.。
这就是在代码中,System.out.println(matcher.group(3));的输出结果是bilibili.而非www.bilibili的原因。
2.4 解决方案:加一层嵌套捕获分组
代码如下
import java.util.regex.*;
public class Main {
public static void main(String[] args) {
String content = "https://www.bilibili.***/video/BV1Eq4y1E79W?spm_id_from=333.788.player.switch&vd_source=dec3407385f13b51755d19fd7f30ac63&p=17";
String regStr = "^((http|https)://)(([\\w]+\\.)+)[\\w]+(\\/[\\w?=.&/]+)?$";
Pattern pattern = Pattern.***pile(regStr);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
System.out.println("完整匹配: " + matcher.group(0));
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
System.out.println(matcher.group(3));
System.out.println(matcher.group(4));
System.out.println(matcher.group(5));
}else{
System.out.println(("匹配失败"));
}
}
}
结果:
新的正则表达式 ^((http|https)://)(([\\w]+\\.)+)[\\w]+(\\/[\\w?=.&/]+)?$ 其分组结构如下:
| 分组编号 | 对应正则部分 | 捕获内容示例 | 说明 |
|---|---|---|---|
| group(0) | 整个表达式 | https://www.bilibili.***/video/BV1Eq4y1E79W?... |
完整匹配的URL |
| group(1) |
((http|https)://) |
https:// | https:// |
| group(2) |
(http|https) |
https |
https |
| group(3) | (([\\w]+\\.)+) |
www.bilibili. |
新增:所有子域名序列 |
| group(4) | ([\\w]+\\.) |
bilibili. |
最后一个子域名 |
| group(5) | (\\/[\\w?=.&/]+) |
/video/BV1Eq4y1E79W?... |
路径及参数 |
关键改进说明
这个解决方案的核心在于将原来的 ([\\w]+\\.)+ 修改为 (([\\w]+\\.)+)。
外层分组 group(3):这个新增的外层分组捕获的是整个 ([\\w]+\\.)+ 序列的匹配结果,即所有子域名(如 www.bilibili.)。
内层分组 group(4):它作为 group(3) 的一个子分组,行为与之前相同,受量词 + 的影响,只保留最后一次匹配的内容(如 bilibili.)。
总结
通过本文的深入探讨,我们系统分析了正则表达式中分组机制的核心原理,特别是针对(pattern)+结构的独特行为。关键点包括:
- 分组基础:正则表达式的分组通过圆括号
()实现,用于捕获和结构化匹配内容,分组编号从左到右依次分配。 - 量词的影响:当分组后跟量词
+或*时,该分组仅保留最后一次成功匹配的结果(如URL中的group(3)返回bilibili.而非www.)。 - 解决方案:通过嵌套捕获分组
(([\\w]+\\.)+),可同时获取完整序列(如www.bilibili.)和末次匹配(如bilibili.),兼顾完整性与精确性。 - 实战价值:此机制在数据处理、验证场景中至关重要,帮助避免信息遗漏,提升匹配效率。
感谢语
衷心感谢各位读者的时间与关注!您的支持是技术分享的最大动力。若本文对您的学习有所启发,欢迎点赞、收藏或留言交流。正则表达式的世界广阔而深邃,让我们继续携手探索,共同进步。