正则表达式高级技巧及实例详解 笨活儿(3)

当正则引擎针对最小组团进行匹配时,它会跳过组团内标记的回溯位置。以单词“smashing”为例,当用上面的正则表达式匹配时,正则引擎会先尝试在“smashing”里寻找“his”。显然,找不到任何匹配。此时,最小组团就发挥作用了:正则引擎会放弃所有回溯位置。也就是说,它不会尝试再从“smashing”里查找“this”。为什么要这样设置?因为“his”都没有返回匹配结果,包含有“his”的 “this”当然就更匹配不了了!

上面的例子并没有什么实用性,我们用/t?his?/ 也能达到效果。再看看下面的例子:

/\b(engineer|engrave|end)\b/

如果把“engineering”拿去匹配,正则引擎会先匹配到“engineer”,但接下来就遇到了字词边界,\b,所以匹配不成功。然后,正则引擎又会尝试在字串里寻找下一个匹配内容:engrave。匹配到eng的时候,后面的又对不上了,匹配失败。最后,尝试 “end”,结果同样是失败。仔细观察,你会发现,一旦engineer匹配失败,并且都抵达了字词边界,“engrave”和“end”这两个词就已经不可能匹配成功了。这两个词都比engineer短小,正则引擎不应该再多做无谓的尝试。

/\b(?>engineer|engrave|end)\b/

上面的替代写法更能节省正则引擎的匹配时间,提高代码的工作效率。

6. 递归(Recursion)

Recursion

递归(Recursion)用于匹配嵌套结构,例如括弧嵌套, (this (that)),HTML标签嵌套<div><div></div></div>。我们使用(?R)来代表递归过程中的子模式。下面是一个匹配嵌套括弧的例子:

/\(((?>[^()]+)|(?R))*\)/

最外层使用了反义符的括号“\(”匹配嵌套结构的开端。然后是一个多选项操作符( * | * ),可能匹配除括号外的所有字符 “(?>[^()]+)”,也可能是通过子模式“(?R)”来再次匹配整个表达式。请注意,这个操作符会尽量多地匹配所有嵌套。

递归的另一个实例如下:

/<([\w]+).*?>((?>[^<>]+)|((?R)))*<\/\1>/

以上表达式综合运用了字符分组,贪婪操作符、回溯,以及最小化组团来匹配嵌套标签。第一个括弧内分组([w]+)匹配出标签名,用于接下来的应用。若找到这尖括号样式的标签,则尝试寻找标签内容的剩余部分。下一个括弧括起来的子表达式和上一个实例非常相似:要么匹配不包括尖括号的所有字符 ?>[^<>]+,要么递归匹配整个表达式(?R)。表达式最后的</1>代表闭合标签。

7. 回调(Callbacks)

Callbacks

匹配结果中的特定内容有时可能会需要某种特别的修改。要应用多重而复杂的修改,正则表达式的回调就有了用武之地。回调是用于函数preg_replace_callback中的动态修改字串的方式。你可以为preg_replace_callback指定某个函数为参数,此函数能接收匹配结果数组为参数,并将数组修改后返回,作为替换的结果。

例如,我们想将某字串中的字母全部转变成大写。十分不巧,PHP没有直接转化字母大小写的正则操作符。要完成这项任务,就可以用到正则回调。首先,表达式要匹配出所有需要被大写的字母:

/\b\w/

上式同时使用了字词边界和字符类。光有这个式子还不够,我们还需要一个回调函数:

function upper_case( $matches ) {
return strtoupper( $matches[0] );
}

函数upper_case接收匹配结果数组,并将整个匹配结果转化成大写。 在此例中,$matches[0]代表需要被大写化的字母。然后,我们再利用preg_replace_callback实现回调:

preg_replace_callback( '/\b\w/', "upper_case", $str );

一个简单的回调即有这般强大的力量。

8. 注释(Commenting)

Commenting

注释不用来匹配字串,但确实是正则表达式中最重要的部分。当正则越写越深入,越写越复杂,要推译出究竟什么东西被匹配就会变得越来越困难。在正则表达式中间加上注释,是最小化将来的迷糊和困惑的最佳方式。

要在正则表达式内部加上注释,使用(?#comment)格式。把“comment”替换成你的注释语句:

/(?#数字)\d/

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wfwyzx.html