熟练掌握正则表达式是每个程序员的基础要求,对于每个初学者来说会被正则表达式一连串字符弄得头晕眼花。博主便会如此,一直对正则表达式有种莫名的恐惧。近来看到另一位博友写的 《php正则表达式》一文获益良多,对其通配符以及捕获数据两个章节颇感兴趣。这两个章节正好涉及到的是正则表达式的捕获组与非捕获组的知识,因而本文来细细探讨下这部分知识。
我们知道,在正则表达式下(x) 表示匹配'x'并记录匹配的值。这只是比较通俗的说法,甚至说这是不严谨的说法,只有()捕获组形式才会记录匹配的值。非捕获组则只匹配,不记录。
捕获组:
(pattern)
这种形式是我们见到最多的一种形式,匹配并返回捕获结果,可以嵌套,组号顺序从左到右依次排列‘。
<span>$regex</span> = '/(ab(c)+)+d(e)?/'<span>; </span><span>$str</span> = 'abccde'<span>; </span><span>$matches</span> = <span>array</span><span>(); </span><span>if</span>(<span>preg_match</span>(<span>$regex</span>, <span>$str</span>, <span>$matches</span><span>)){ </span><span>print_r</span>(<span>$matches</span><span>); }</span><span><br /></span>
匹配结果:
<span>Array</span> ( [0] => abccde [1] => abcc [2] => c [3] => e )
(?P50ff0fbfa061d7a95bd542ac40874614pattern)
这种方式虽然看起来在构造正则表达式的时候略微复杂一点,但实质上与(pattern)一样。最大的优势体现在对结果处理上,程序员可以直接根据自己设置的8a11bc632ea32a57b3e3693c7987c420直接快速调用结果,而不用再去数需要的结果在第几个子组了。
<span>$regex</span> = '/(?P<group1>\w(?P<group2>\w))abc(?P<group3>\w)45/'<span>; </span><span>$str</span> = 'fsabcd45'<span>; </span><span>$matches</span> = <span>array</span><span>(); </span><span>if</span>(<span>preg_match</span>(<span>$regex</span>, <span>$str</span>, <span>$matches</span><span>)){ </span><span>print_r</span>(<span>$matches</span><span>); } </span>
匹配结果:
<span>Array</span> ( [0] => fsabcd45 [group1] => fs [1] => fs [group2] => s [2] => s [group3] => d [3] => d )
\num
num是一个整数,是对捕获组的反向引用。 例如\2表示第二个子组匹配值,\表示第一个子组匹配值
<span>$regex</span> = '/(\w)(\w)\2\1/'<span>; </span><span>$str</span> = 'abba'<span>; </span><span>$matches</span> = <span>array</span><span>(); </span><span>if</span>(<span>preg_match</span>(<span>$regex</span>, <span>$str</span>, <span>$matches</span><span>)){ </span><span>print_r</span>(<span>$matches</span><span>); }</span>
匹配结果:
<span>Array</span> ( [0] => abba [1] => a [2] => b )
注意,这里我疏忽了一个小细节,一开始我第一样代码是 $regex = “/(\w)(\w)\2\1/”; 结果返回无匹配结果,经过调试后,发现这里只能用' '。'与" 用法差别大家还是需要注意下。
\k42b062ee5cf8d6bf92b220928e3c55ba
了解了(?P50ff0fbfa061d7a95bd542ac40874614pattern)与\num,这个就不难理解了。\k6521b4091391a7a21eb2d14fa971d747是对命名捕获组的反向引用。其中 name 是捕获组名。
<span>$regex</span>='/(?P<name>\w)abc\k<name>/'<span>; </span><span>$str</span>="fabcf"<span>; </span><span>echo</span> <span>preg_match_all</span>(<span>$regex</span>, <span>$str</span>,<span>$matches</span><span>); </span><span>print_r</span>(<span>$matches</span>);
匹配结果:
Array ( [0] => Array ( [0] => fabcf ) [name] => Array ( [0] => f ) [1] => Array ( [0] => f ) )
非捕获组:
(?:pattern)
与(pattern)的唯一区别是,匹配pattern但不捕获匹配结果。这里便不再举例。
还有四种方式实际上讲的是一个事情:预查。
预查分为正向预查与反向预查。根据字面理解,正向预查是判断匹配字符串后面某些字符存在与否,而反向预查则是判断匹配字符串前面某些字符存在与否。
正向预查判断存在使用(?=pattern),判断不存在使用(?!pattern)。
反向预查判断存在使用(?<=pattern),判断不存在使用(?pattern)。
<span>$regx</span>='/(?<=a)bc(?=d)/'<span>; </span><span>$str</span>="abcd ebcd abce ebca"<span>; </span><span>if</span>(<span>preg_match_all</span>(<span>$regx</span>, <span>$str</span>, <span>$matches</span><span>)){ </span><span>print_r</span>(<span>$matches</span><span>); }</span>
匹配结果:
<span>Array</span> ( [0] => <span>Array</span> ( [0] => bc) )
这四种形式使用的是否只要注意好相对匹配字符串的位置和断言肯定还是否定,就会很快掌握。
另外,预查的四种形式是零宽度的,匹配的时候只做一个判断,本身是不占位置的。/HE(?=L)LLO/ 与HELLO匹配,而/HE(?=L)LO/与HELLO是不匹配的。毕竟但从字节数上两者就是不匹配的,前者只有4个,而后者有5个。