捕获组和非捕获组

捕获组

捕获组,通过括号将正则表达式括起来,正则表达式匹配成功后,可以通过组号来获取相应的匹配内容。如:我们想获取数字中的整数部分和小数部分,可以用这样的正则表达式。

String regex = "(\\d+)\\.(\\d+)";
String content = "12.98";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
    System.out.println("数字:" + matcher.group());
    System.out.println("整数部分 : " + matcher.group(1));
    System.out.println("小数部分 : " + matcher.group(2));
}

这里用Matcher 中的 group(1),可以获取整数部分,用group(2)可以获取小数部分,如果group()中不填数字,则默认获取整个匹配组,即整个数字。

这里,我们用序号来获取相应的捕获组,但其实也可能给每个捕获组命名,通过名称来获取捕获组。

String regex = "(?<one>\\d+)\\.(?<two>\\d+)"; //?<name>指定组名称
String content = "12.98";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
    System.out.println("数字:" + matcher.group());
    System.out.println("整数部分 : " + matcher.group("one"));
    System.out.println("小数部分 : " + matcher.group("two"));
}

非捕获组

非捕获组,即不要获取括号里的内容。

问1:既然不要获取括号里的内容,那在正则表达式中不加括号不就可以吗?

正则表达式中的括号,不仅用来分组,有些场景下,不加括号并不能写出你想要的效果。

举个例子:String regex = "12(a|b)12";

这个正则表达式,匹配的是开头和结尾是“12”,中间字符是a或b,如果没有括号,那就成:

String regex = "12a|b12";

匹配的结果就是字符“12a”或“b12”。

问2:那如果加括号后,我不用group()获取分组,不也可以吗?

语法上没错,但用了括号来捕获字符串,则会占用一定的内存去记录这些捕获组,如果我们实际上不需要这些捕获组,那可以不让内存记录着,提高程序效率 。

非捕获组有以下几种情况:

  • (?:)   非捕获组
  • (?=) 肯定式向前查找
  • (?!) 否定式向前查找
  • (?<=) 肯定式向后查找
  • (?<!) 否定式向后查找

用了非捕获组,如果再用group(int index) 去获取匹配的内容,则会报错。

(?:)   非捕获组

例子:获取abc或adc字样的字符串。

捕获组的例子如下:

String regex = "a(b|d)c";
String content = "adcdddd";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
if(matcher.find()){
    System.out.println("用group(1)获取第一个括号的内容 :" + matcher.group(1));
}

如果我们并不需要获取括号里的内容,那可以把这个括号写成一个非捕获组,如下:

String regex = "a(?:b|d)c";
String content = "adcdddd";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
if(matcher.find()){
    System.out.println("匹配到的内容 :" + matcher.group(1));
}

注意,运行此程序,会报以下错,表示并没有group1 这个组。这是因为我们用了非捕获组,括号里的内容就不会进行记录,用group(1)自然就获取不到

(?=) 肯定式向前查找

这里所说的向前,是指字符串从左到右的方向。如,查找被<html> </html> 包围着的字符串。

我们可以先查询到字符串<html>,再向前查</html>。

String regex = "\\<html\\>(.*)(?=\\</html\\>)";
String content = "<html> hello world </html>";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
    System.out.println("match : " + matcher.group(1));
} else {
    System.out.println("not match ");
}

运行结果为: match :  hello world 

'?=.'的意思是断言;只匹配一个位置

比如,你想匹配一个“人”字,但是你只想匹配中国人的人字,不想匹配法国人的人;就可以用断言做;

例子:必须包含大小写字母和数字的组合,后面的{8,10}表示长度

Pattern pattern = Pattern.compile("^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$");
Matcher matcher = pattern.matcher("1aA12BC1");
while (matcher.find()) {
    System.out.println(matcher.group());
}

(?!) 否定式向前查找

跟肯定式向前查找相反,是查找前面没有特定字符串的内容。

比如,查找出现“Java” 字符串且后面没有出现“Python” 的字符串。

String regex = "Java(?!.*Python.*)";
String content = "Java and Python are both good languages";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
    System.out.println("match");
} else {
    System.out.println("not match ");
}

此程序运行的结果是:not match 

如果把字符串换成:Java and C++ are both good languages,则运行结果是 : match。

 (?<=) 肯定式向后查找

查找前面有一个数字的英文字符串。

String regex = "(?<=\\d)([a-z]+)";
String content = "abcd2def";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
    System.out.println("match : " + matcher.group(1));
} else {
    System.out.println("not match ");
}

运行结果为:

match : def

(?<!) 否定式向后查找

不以end结尾的字符串。

String regex = "(.*)(?<!end)$";
String content = "python language end";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
    System.out.println("match : " + matcher.group(1));
} else {
    System.out.println("not match ");

}

运行结果为:

not match 

posted @ 2023-08-20 08:54  残城碎梦  阅读(86)  评论(0编辑  收藏  举报