PHP Efficiency Issues
PHP的preg程序使用的PCRE是经过优化的NFA正则引擎,所以第4到6章介绍的许多技巧都可以直接应用。其中包括对关键部分进行性能测试,根据实际数据而不是从理论分析来比较程序的快慢。第6章给出了PHP的性能测试的例子(☞234)。
如果程序对时间要求很严格,请记住两点,回调函数通常要比模式修饰符e更快(☞465),在太长的目标字符串中使用命名捕获必须进行更多的数据拷贝。
程序运行中,遇到正则表达式会编译,但是PHP 有一个容量高达4 096 个正则表达式的大型缓存(☞242),所以实际上,特殊的pattern字符串只需要在第一次遇到的时候编译。
模式修饰符S值得单独介绍:它会“研究(study)”一个正则表达式,试图进行更快的匹配(它与Perl的study函数不相关,Perl的study函数研究的是目标字符串,而不是正则表达式☞359)。
模式修饰符S:“研究”
The S Pattern Modifier:"Study"
使用模式修饰符S告诉正则引擎,在应用这个正则表达式之前,花一点时间(注5)来研究,希望这些多花的时间是值得的。但是,也可能这样做之后也不会提升速度,但是某些情况下,速度的提升是与数据规模相关的。
现在有良好的标准来判断哪种情况下此功能具有价值:它是第 6 章所说的开头字符组识别优化(☞247)的增强。
首先要告诉你的是,除非你希望对大规模的文本应用某个正则表达式,否则不太可能节省多少时间。只有把同一个表达式应用到大规模的文本,或者大量小规模文本时,才需要考虑模式修饰符S。
不使用模式修饰符S,标准优化
来看个简单的例子「<(/w+)」。从这个表达式中我们可以看出,每次匹配必须以字符‘<’开头。正则引擎能够(preg套件必然会这样做)利用这一点,预先在目标字符串中搜索‘<’,只在这些位置应用完整的正则表达式(因为匹配必须以「<」开头,在其他位置进行匹配尝试是徒劳的)。
使用简单预搜索可以比按部就班应用整个正则表达式快得多,原因就在于这种优化。尤其是,需要搜索的字符在目标字符串中出现的次数越少,优化越明显。同样,正则引擎判断第一个字符匹配失败的工作量越大,优化越明显。这种优化对「<i>|</i>|<b>|</b>」比对「<(/w+)」更明显,因为在进行下一轮尝试之前,正则引擎会尝试搜索4个不同的多选分支,这样可以减少许多工作量。
使用模式修饰符S进一步优化
preg 引擎足够聪明,能把这种优化应用到大多数正则表达式,它们的匹配必须以某个字符开头,就像上面的例子一样。不过,模式修饰符 S 告诉引擎,对于可能以多个字符开头的表达式,必须首先分析正则表达式来启用这种优化。
这里有几个正则表达式的例子,其中有一些在本章中已经出现过,使用模式修饰符 S 的结果如下:
模式修饰符S没有用处的场合
想想在哪些情况下模式修饰符S没有用会很有其法:
●开头有锚点的表达式(例如「^」和「/b」),或者一个锚点紧跟全局性多选分支。这限于当前的实现,「/b」的限制,理论上在未来的某些版本中可以去掉。
●能够匹配空字符的表达式,例如「/S*」。
●表达式可以从任何字符开始匹配(或者是绝大多数字符),例如「(?:[^()]++|/((?R)/))* 」,请参考第 475 页的例子。这个表达式能够从除‘)’之外的任何字符开始匹配,所以预先检查一遍几乎不会去掉任何开始的位置。
●开头字符只有一种可能的表达式,因为它们已经进行了优化。
使用建议
使用模式修饰符 S 之后,preg 引擎花在预分析上的时间并不会太长,所以如果你希望对大量的文本应用正则表达式,无妨使用它。如果你觉得有机会使用,潜在的可能就值得尝试。