💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
**分组()** 匿名分组:`(partten)` 上面说过重复一个字符0次、一次或多次,那么如何重复多个字符呢,就是我们要说的分组,例如要匹配一段IP地址(简单的IP匹配):(/d{1,3}/.){3}/d{1,3}.其中()括起来的部分重复3次。 分组时指定组名: ``` (?<group>parttern) (?'groupname'parttern) (?P<groupname>parttern) ``` 例子: ``` $subject="AAABBCCAAAADDE666FF"; $pattern="/(\w)/"; //匿名分组 $pattern="/(?<name>\w)/"; $pattern="/(?P<name>\w)/"; $pattern="/(?'name'\w)/"; //只能单引号 $a=preg_match_all($pattern, $subject, $matches, PREG_OFFSET_CAPTURE); dump($matches); ``` ``` $subject=" PDO::ATTR_TIMEOUT(integer) 设置连接数据库的超时秒数。 PDO::ATTR_ERRMODE(integer) 关于此属性的更多信息请参见 错误及错误处理 部分。"; $pattern="/(?P<pdo>PDO)::(?P<pro>[^)(]+)+\((?P<type>[^)(]+)+\)\s+(?P<desc>[^\r\n]+)/"; preg_match_all($pattern,$subject ,$matches); ``` **组名的反向引用** 表达式在匹配时,表达式引擎会将小括号 "( )" 包含的表达式所匹配到的字符串记录下来。在获取匹配结果的时候,小括号包含的表达式所匹配到的字符串可以单独获取引用方法是 "\\" 加上一个数字。"\\1" 引用第1对括号内匹配到的字符串,"\\2" 引用第2对括号内匹配到的字符串……以此类推,如果一对括号内包含另一对括号,则外层的括号先排序号。换句话说,哪一对的左括号 "(" 在前,那这一对就先排序号 <table border="1" cellspacing="0" cellpadding="6"> <tbody> <tr><th> <p>反向引用</p> </th><th> <p>说明</p> </th></tr> <tr> <td> <p><strong>\n</strong></p> </td> <td> <p>对指定数字编号的分组进行反向引用</p> </td> </tr> <tr> <td> <p><strong>\g&lt;name&gt;</strong></p> </td> <td rowspan="3"> <p>对指定名字的命名分组进行反向引用</p> </td> </tr> <tr> <td> <p><strong>\k&lt;name&gt;</strong></p> </td> </tr> <tr> <td> <p><strong>\k'name'</strong></p> </td> </tr> </tbody> </table> ## 从一个简单例子说起 ``` $subject="abcdebbcde"; $pattern="/([ab])\\1/";  //  注意php的\\1前还需要加个\转义"\" 等同 $pattern="/([ab])([ab])/"; ``` 对于正则表达式“**(\[ab\])\\1**”,捕获组中的子表达式“**\[ab\]**”虽然可以匹配“**a**”或者“**b**”,但是捕获组一旦匹配成功,反向引用的内容也就确定了。如果捕获组匹配到“**a**”,那么反向引用也就只能匹配“**a**”,同理,如果捕获组匹配到的是“**b**”,那么反向引用也就只能匹配“**b**”。由于后面反向引用“**\\1**”的限制,要求必须是两个相同的字符,在这里也就是“**aa**”或者“**bb**”才能匹配成功。 考察一下这个正则表达式的匹配过程,在位置0处,由“**(\[ab\])**”匹配“**a**”成功,将捕获的内容保存在编号为1的组中,然后把控制权交给“**\\1**”,由于此时捕获组已记录了捕获内容为“**a**”,“**\\1**”也就确定只有匹配到“**a**”才能匹配成功,这里显然不满足,“**\\1**”匹配失败,由于没有可供回溯的状态,整个表达式在位置0处匹配失败。 正则引擎向前传动,在位置5之前,“**(\[ab\])**”一直匹配失败。传动到位置5处时,,“**(\[ab\])**”匹配到“**b**”,匹配成功,将捕获的内容保存在编号为1的组中,然后把控制权交给“**\\1**”,由于此时捕获组已记录了捕获内容为“**b**”,“**\\1**”也就确定只有匹配到“**b**”才能匹配成功,满足条件,“**\\1**”匹配成功,整个表达式匹配成功,匹配结果为“**bb**”,匹配开始位置为5,结束位置为7。 扩展一下,正则表达式“**(\[a-z\])\\1{2}**”也就表达连续三个相同的小写字母。 ~~~ $subject="abc 123abcd"; $pattern="/(abc)(\s)(\d)+?\\1/";//\1就是第一个()组匹配到的内容 注意需要将\转义下 $a=preg_match_all($pattern, $subject, $matches, PREG_OFFSET_CAPTURE); var_export($matches);//匹配到abc 123abc ~~~  更多反向引用实例 ~~~ $subject="AAABBCCAAAADDE666FF"; $pattern="/(\w)/"; $a=preg_match_all($pattern, $subject, $matches, PREG_OFFSET_CAPTURE); var_export($matches); //['A','A','A','B','B','C','C','A','A','A','A','D','D','E','6','6','6','F','F'; $pattern="/(\w)\\1/"; //array ( 'AA', 'BB', 'CC','AA', 'AA', 'DD', '66','FF',) $pattern="/(\w)\\1*/"; //[ 'AAA', 'BB', 'CC', 'AAAA', 'DD', 'E', '666', 'FF' ] $pattern="/(\w)+/"; //[ 'AAA', 'BB', 'CC', 'AAAA', 'DD', '666', 'FF' ] $pattern="/(\w)(\d)/"; //[ 'E6', '66' ] $pattern="/(\w)(\d)\\1/"; //array ( '666') $pattern="/(\w)(\d)\\1*/"; //[ 'E6', '66' ] $pattern="/(\w)(\d)\\1+/"; //[ '666' ] $pattern="/(\w)(\d)\\1\\2*/"; //[ 'E666' ] $pattern="/(\w)(\d)\\1\\2+/"; //[ 'E666' ] ~~~ ~~~ $subject="aaa bbbb ffffff 999999999"; $pattern="/(\w)((?=\\1\\1\\1)(\\1))+/"; $a=preg_match_all($pattern, $subject, $matches); var_export($matches); # [ 'bb', 'ffff', '9999999'] # (?=exp)非获取匹配的正向肯定预查 匹配\1\1\1左边的\w 而\1 又是第一个\w \1\1\1可知至少三个相同的\w字符 后面的(\\1)+可知至少一个 # (\w)((?=\\1\\1\\1) 三个相同的\w字左边还有一个相同的\为字符 即符四个相同的\w字符 # ~~~