正则表达式的高级技巧分享(2)


preg_match( ' /(?p< quote> " |' )/' " ' string' " $matches )

# 下面的语句输出“' ”(不包括双引号)
echo $matches[1]

# 使用组名调用,也会输出“' ”
echo $matches[' quote' ]


所以,有名组并不只是让写代码更容易,它也能用于组织代码。

4. 字词边界(word boundaries)

字词边界是字串里的字词字符(包括字母、数字和下划线,自然也包括汉字)和非字词字符之间的位置。其特殊之处就在于,它并不匹配某个实在的字符。它的长度是零。 b 匹配所有字词边界。

不幸的是,字词边界一般都被忽视掉了,大部分人都没有在意他的现实意义。 例如,如果你想要匹配单词“import”:

1./import/
注意了!正则表达式有时候很调皮的。下面的字串也能和上面的式子匹配成功:

1.important
你或许觉得,只要在import前后加上空格,不就可以匹配这个独立的单词了:

1./ import /
那如果遇上这种情况呢:

1.the trader voted for the import
当 import 这个词在字串开头或者结尾时,修改后的表达式仍然不能用。因此,考虑各种情况是必须的:

1./(^import | import | import$)/i
别慌,还没完呢。如果遇到标点符号了呢?就为了满足这一个单词的匹配,你的正则可能就需要这样写:

1./(^import(:| | )? | import(:| | )? | import(.|?|!)?$)/i
对于只匹配一个单词来说,这样做实在是有点大动干戈了。正因如此,字词边界才显得意义重大。要适应上述要求,以及很多其他情况变种,有了字符边界,我们所需写的代码只是:

1./bimportb/
上面所有情况都得到了解决。 b 的灵活性就在于,它是一个没有长度的匹配。它只匹配两个实际字符之间想象出的位置。它检查两个相邻字符是否是一个为单字,另一个为非单字。情况符合,就返回匹配。如果遇到了单词的开头或结尾, b 会把它当成是非单词字符对待。由于import里面的 i 仍然被看成是单词字符,import 就被匹配出来了。

注意,与b相对,我们还有 b,此操作符匹配两个单字或者两个非单字之间的位置。因此,如果你想匹配在某个单词内部的‘hi',可以使用:

1.bhib
“this”、“hight”,都会返回匹配,而“hi there”则会返回不匹配。

5. 最小组团(atomic groups)

最小组团是无捕捉的特殊正则表达式分组。通常用来提高正则表达式的效能,也能用于消除特定匹配。一个最小组团可以用(?> pattern) 来定义,其中pattern是匹配式。

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

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

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

1./b(?> engineer|engrave|end)b/
上面的替代写法更能节省正则引擎的匹配时间,提高代码的工作效率。

6. 递归(recursion)

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

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

递归的另一个实例如下:

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

7. 回调(callbacks)

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

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

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

复制代码 代码如下:

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

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