捕获和分组
分组和捕获
分组是将正则表达式中的一部分放在小括号()
之内,从而将该部分组合在一起。分组内的正则表达式匹配的内容是连续的一个整体,比如说 /foot(ball)/
可以也只能匹配football
。和[]
不同,[]
里的正则不是连续的一个整体,比如/foot[ball]/
,其中[ball]
里面的每个字符是或的关系,它可以匹配的footb
、foota
、footl
,如果再加上量词+
/foot[ball]+/
可以匹配foot
后面跟着的b
、a
和l
三个字符的任意组合。
下面回归到分组。分组内的正则是一个整体,如果想要可选分支,可以使用竖线|
来实现。比如/foot(ball|age)/
可以匹配 football
或者footage
。如果我们不使用()
,则/football|age/
只能匹配football
或者 age
。这就是分组
的其中的一个作用。
分组的第二个作用就是捕获
。在()
中的正则匹配到的字符串会被保存起来,在正则表达式中可以继续引用这部分被捕获的字符串(反向引用
)。被捕获的内容的下标从1开始计数。/foot(ball)/
匹配字符串football
成功以后,下标0是整个正则表达式匹配到的内容,下标1是由()
捕获的内容ball
。对于下标的计数方式,遵循的原则是:嵌套从外到内,并列从左到右。什么意思呢,分组是可以嵌套的,如果正则中有分组嵌套分组的情形,那最外层的分组要先开始计数。并列的从左到右很好理解了,左边的要先于右边的。下面我们看/((foot|basket)(ball))/
匹配字符串football
。最外层的分组下标为1,内容是football
,里层分组遵循从左至右原则,下标2为foot
,下标3为ball
。这里需要注意的是嵌套的优先级要高于并列的。就相当于深度遍历。如果一个分组1嵌套的有分组2,并且在分组1的右边并列又个分组3,则分组2的下标要先于分组3。/((foot|basket)(ball))\s(ok)/
匹配字符串football ok
,分组((foot|basket)(ball))
的下标不会变,(ok)
的下标是4。
<?php
$str = "football ok";
$pattern = "/((foot|basket)(ball))\s(ok)/";
$res = preg_match($pattern,$str,$matches);
print_r($matches);
执行结果
Array
(
[0] => football ok
[1] => football
[2] => foot
[3] => ball
[4] => ok
)
注: 捕获分组的最大下标序号是65535,也就是说最大可以有65535个分组。然而现实中我们不会在一个正则表达式中使用这么多的分组。
既然分组可以对分组内匹配到的字符串进行捕获,那如果我们仅仅是对正则进行分组,而不对其捕获,这也是可以的。非捕获分组形式为(?:)
。对于上面的例子,如果不想捕获football
,可以修改正则为/(?:(foot|basket)(ball))\s(ok)/
<?php
$str = "football ok";
$pattern = "/(?:(foot|basket)(ball))\s(ok)/";
$res = preg_match($pattern,$str,$matches);
print_r($matches);
再看执行结果 football 就不会被捕获了。
Array
(
[0] => football ok
[1] => foot
[2] => ball
[3] => ok
)
在PHP中,对于分组(不管是捕获还是非捕获),还提供了另一种功能。可以给分组单独指定模式修饰符。在分组的开始部分加上(?修饰符)
。 捕获分组形式((?修饰符)regex)
;非捕获分组形式(?:(?修饰符)regex)
。 对于非捕获分组还有另一种形式,就是在 ?
和 :
之间加修饰符——(?修饰符:)
。 例如让foot
或者basket
不区分大小写,/(((?i)foot|basket)(ball))\s(ok)/
。
<?php
$str = "FOOtball ok";
$pattern = "/(((?i)foot|basket)(ball))\s(ok)/";
$res = preg_match($pattern,$str,$matches);
print_r($matches);
匹配结果
Array
(
[0] => FOOtball ok
[1] => FOOtball
[2] => FOOt
[3] => ball
[4] => ok
)
非捕获两种形式
<?php
$str = "FOOtball ok";
$pattern = "/((?:(?i)foot|basket)(ball))\s(ok)/";
// 或者 "/((?i:foot|basket)(ball))\s(ok)/"
$res = preg_match($pattern,$str,$matches);
print_r($matches);
执行结果
Array
(
[0] => FOOtball ok
[1] => FOOtball
[2] => ball
[3] => ok
)
捕获内容的命名
对于捕获的内容除了使用下标进行索引之外,PHP还提供了给捕获命名的功能。语法形式为(?<name>)
或者(?'name')
。
/((?<type>foot|basket)(ball))\s(ok)/
给分组命名为type
。那么除了对捕获的内容进行下标索引之外,还能使用键名进行关联。
<?php
$str = "football ok";
$pattern = "/((?<type>foot|basket)(ball))\s(ok)/";
$res = preg_match($pattern,$str,$matches);
print_r($matches);
执行结果
Array
(
[0] => football ok
[1] => football
[type] => foot
[2] => foot
[3] => ball
[4] => ok
)