正则大神主页:https://blog.youkuaiyun.com/lxcnn?t=1
正则表达式入门教程:https://deerchao.cn/tutorials/regex/regex.htm#top
上述两个博客的文章都是以C#为基础写作的,在普通捕获组与命名捕获组的混用上,与Java有所不同
正则运行测试网址:https://regex101.com/
正则中,每个用于匹配字符的()都是一个子表达式,可以被捕获分组捕获到,从而在正则表达式内或表达式外进行引用。环视使用了(),但是它是用来匹配位置信息的,所以不会被捕获;还有就是使用(?:expression)
来主动忽略分组。
更新:
在正则表达式本身中使用分组,默认编号分组-\d{4}([:-])\d{2}\1\d{2}
;命名分组-\d{4}(?<d>[-:])\d{2}(\k<d>)\d{2}
普通捕获组
正则表达式中,普通捕获组是按照(
即左括号出现的顺序进行分组。对类似“2016-01-06”格式的日期进行简单匹配并分组,暂不不考虑闰年等问题。

分组 | 分组内容 |
---|---|
分组0 | (\d{4})-(\d{2})-(\d{2}) |
分组1 | \d{4} |
分组2 | \d{2} |
分组3 | \d{2} |
对于所有的正则表达式,捕获组0都是正则表达式匹配的全部内容,然后第一对括号内包含的匹配内容是捕获组1,第二对括号内是捕获组2,第三对括号内是捕获组3.
Java实例代码如下:
String str = "2016-01-06";
Pattern pattern = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher matcher = pattern.matcher(str);
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));
}
// 结果如下:
// 2016-01-06
// 2016
// 01
// 06
命名捕获组
还是对日期进行正则匹配,表达式本身不变,改为使用命名捕获组组。命名捕获组与普通捕获组的区别在于,在()内添加捕获组的名字,方便后续使用。格式为:(?<name>expression)
。name区分大小写。

分组 | 分组内容 |
---|---|
分组year | \d{4} |
分组month | \d{2} |
分组day | \d{2} |
通过在()内添加?<name>
,对捕获组进行命名,从而更方便后续对匹配内容的使用。
Java实例代码如下:
String str = "2016-01-06";
Pattern pattern = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})");
Matcher matcher = pattern.matcher(str);
if (matcher.find()){
System.out.println(matcher.group("year"));
System.out.println(matcher.group("month"));
System.out.println(matcher.group("day"));
}
// 结果如下:
// 2016
// 01
// 06
普通捕获组与命名捕获组混用
当普通捕获组与命名捕获组混用时,各个语言的具体规则略有差异。
在Java中,不管此捕获组是否被命名,依旧按照普通捕获组的规则进行排序,然后被命名的分组同时也可以作为命名捕获组使用。

普通捕获组 | 命名捕获组 | 分组内容 |
---|---|---|
分组0 | (\d{4})-(\d{2})-(\d{2}) | |
分组1 | \d{4} | |
分组2 | 分组month | \d{2} |
分组3 | \d{2} |
Java实例代码如下:
String str = "2016-01-06";
Pattern pattern = Pattern.compile("(\\d{4})-(?<month>\\d{2})-(\\d{2})");
Matcher matcher = pattern.matcher(str);
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("month"));
}
// 结果如下:
// 2016-01-06
// 2016
// 01
// 06
// 01
通过上述代码可以看到,月份01既可以作为普通捕获组2使用,同时也可以作为命名捕获组month使用。
C#的普通捕获组与命名捕获组混用时的规则与Java不一致。在C#中,会先去除已被命名的捕获组,对剩余的所有捕获组按照普通捕获组的规则进行排序,然后再对命名捕获组进行排序,因此在C#中,所有命名捕获组的序号都会大于普通捕获组的序号。
在上述例子中,已被命名为month的捕获组,在C#中序号应该是分组3,而不是分组2,分组2应该是用于匹配日期的子表达式。
非捕获组
在正则中,可以选择忽略某些捕获组,不将它们列入分组捕获排序中,从而更方便的获取我们想要匹配的内容。子表达式中增加?:,会被正则引擎忽略,不被计入捕获组中。(?:expression)
这种格式的子表达式被称为非捕获组。

普通捕获组 | 非捕获组 | 分组内容 |
---|---|---|
分组0 | (\d{4})-(\d{2})-(\d{2}) | |
分组1 | \d{4} | |
非捕获组 | ?:\d{2} | |
分组2 | \d{2} |
Java实例代码如下:
String str = "2016-01-06";
Pattern pattern = Pattern.compile("(\\d{4})-(?:\\d{2})-(\\d{2})");
Matcher matcher = pattern.matcher(str);
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));
}
// 结果如下:
// 2016-01-06
// 2016
// 06
// Exception in thread "main" java.lang.IndexOutOfBoundsException: No group 3
通过代码可以看到,匹配月份的子表达式被正则引擎忽略,并没有计入捕获组的排序中。其他子表达式依旧按照正常的分组排序规则进行排序,匹配日期的子表达式作为分组2被统计,导致缺少分组3,在获取时抛异常。
总结
正则表达式的捕获组内容包括有普通捕获组,命名捕获组,普通捕获组与命名捕获组混用,非捕获组。
上一篇:贪婪、非贪婪与占有模式
下一篇:反向引用