Java 正则表达式全攻略(五)
labels:Java 正则表达式全攻略(五) java正则表达式 捕获性分组 后引用 非捕获性分组
捕获组
捕获组就是把正则表达式中的一部分用“()”括起来形成组,然后你可以对整个组使用一些正则操作,例如重复操作符。捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 (A)(B(C)) 中,存在四个这样的组:
0 | (A)(B(C)) |
1 | (A) |
2 | (B(C)) |
3 | (C) |
如果把表达式改为 ((A)(B(C))) ,则存在五个这样的组:
0 | ((A)(B(C))) |
1 | ((A)(B(C))) |
2 | (A) |
3 | (B(C)) |
4 | (C) |
其中0组始终代表整个表达式。我们可以通过下面的代码实例来进一步理解:
1: Pattern p = Pattern.compile("(\\w+)@(\\w+\\.\\w+)" ); // 一个用于匹配邮件地址的简单表达式
2: Matcher m = p.matcher("gzyangfan@gmail.com" );
3: m.matches(); // 进行匹配
4: assertThat(m.groupCount(), is(2)); // 返回捕获组数,该表达式不算0组共有2个捕获组
5: assertThat(m.group(0), equalTo("gzyangfan@gmail.com" )); // 0组永远代表整个表达式
6: assertThat(m.group(1), equalTo("gzyangfan" )); // 1组代表邮箱名
7: assertThat(m.group(2), equalTo("gmail.com" )); // 2组代表网站名
捕获组还可以对整个组进行表达式操作,例如重复,我们看下面这个例子:
1: String regex = "(cat){1,3}" ;
2: assertThat("catcatcat" .matches(regex), is(true));
3: assertThat("catcat" .matches(regex), is(true));
4: assertThat("cat" .matches(regex), is(true));
5: assertThat("dog" .matches(regex), is(false));
这个例子里我们定义了一个捕获组,并通过数量词,允许这个组整体出现1次到3次。
后引用
当用“()”定义了一个正则表达式组后,正则引擎则会把被匹配的组按照顺序编号,存入缓存。当对被匹配的组进行向后引用的时候,可以用“\数字”的方式进行引用。 \1 引用第一个匹配的后引用组, \2 引用第二个组,以此类推, \n 引用第n个组。而 \0 则引用整个被匹配的正则表达式本身。我们看一个例子。
1: String string = "联系信息:020-81234567gzyangfan@gmail.com" ;
2:
?
3: // 范例一
4: Pattern p1 = Pattern.compile("<\\w+>.*?" );
5:
Matcher m1 = p1.matcher(string);
6:
m1.find();
7: assertThat(m1.group(), is("020-81234567" ));
8:
m1.find();
9: assertThat(m1.group(), is("gzyangfan@gmail.com" ));
10:
?
11: // 范例二
12: Pattern p2 = Pattern.compile("<(\\w+)>.*?" );
13:
Matcher m2 = p2.matcher(string);
14:
m2.find();
15: assertThat(m2.group(), is("020-81234567" ));
16:
assertThat(m2.find(), is(false));
我们可以看到范例一中的表达式无法正确判断结束标签是否与开始标签一致,在代码第9行中将一个不合法的内容也匹配出来了。而范例二用过使用捕获组和对组进行后引用,使错误的标签不会被匹配出来,可见向后引用是非常有用的功能。
不过我们还是需要注意一下后引用的一些要求:
-
- 一个后向引用不能用于它自身。([abc]\1) 是错误的。因此你不能将 \0 用于一个正则表达式匹配本身,它只能用于替换操作中。
- 后向引用不能用于字符集内部。(a)[\1b] 中的 \1 并不表示后向引用。在字符集内部,\1 可以被解释为八进制形式的转码。
非捕获组
后引用会降低引擎的速度,因为它需要存储匹配的组。如果你不需要后引用,你可以告诉引擎对某个组不存储,即将其声明为非捕获组。例如:Get(?:Value) 。其中“(”后面紧跟的“?:”会告诉引擎组“(Value)”为非捕获组,不存储匹配的值以供后引用。