1、使用字符组代替分支条件
想要匹配“a~g”之间的字母时,应该使用 [a-g]
来表示,而不是使用 (a|b|c|d|e|f|g)
表示,下面通过示例代码来说明以下二者执行效率之间的差异:
<?php $cnt=1000; $testStr=""; for($i=0;$i<1000;$i++){ $testStr.="abababcdefg"; } //第一种方案 $start=microtime(TRUE); for($i=0;$i<$cnt;$i++){ preg_match('#^(a|b|c|d|e|f|g)+$#',$testStr); } echo '第一种方案执行时间:'.(microtime(TRUE)-$start).'<br>'; //第二种方案 $start=microtime(TRUE); for($i=0;$i<$cnt;$i++){ preg_match('#^[a-g]+$#',$testStr); } echo '第二种方案执行时间:'.(microtime(TRUE)-$start).'<br>'; //第三种方案,与第二种方案本质上是相同的 $start=microtime(TRUE); for($i=0;$i<$cnt;$i++){ preg_match('#^[abcdefg]+$#',$testStr); } echo '第三种方案执行时间:'.(microtime(TRUE)-$start).'<br>'; ?>
运行结果如下所示:
第一种方案执行时间:0.017441034317017
第二种方案执行时间:0.0054929256439209
第三种方案执行时间:0.0054469108581543
通过运行结果可以看出,[a-g]
与 [abcdefg]
这两种正则表达式的执行效率是差不多的,它们都比使用(a|b|c|d|e|f|g)
这样的分支结构速度要快。这是因为在匹配单个字符的时候,引擎会把 [abc]
这样的字符组视为 1 个元素,而不是 3 个元素。整个元素作为匹配迭代的一个单元,不需要进行三次迭代,从而提高匹配效率。
2、将容易匹配的结果左移
当需要使用类似 (a|b|c)
这样的分支条件时,尽量将容易匹配的条件放在最左边。因为对于传统型 NFA 引擎来说(PHP 中的 preg 系列函数就属于传统型 NFA 引擎),会从左到右匹配每个分支条件,当匹配到满足的分支条件时,就会停止不再考虑其它的条件了。
3、标准量词是匹配优先
若用量词约束某个表达式,那么在匹配成功前,进行的尝试次数是有下限和上限的。例如,正则表达式为:
preg_match('//w*(/d+)/','copy2003y',$match);
正则表达式中 (/d+)
的匹配结果是多少呢?大家可能认为是 2003,其实并不是,它的结果应该 3,下面就来分析以下:
当使用正则表达式/w*(/d+)
来匹配字符串“copy2003y”时,会先用/w*
匹配字符串“copy2003y”。而/w*
会匹配字符串“copy2003y”中的所有字符,然后再交给/d+
来匹配剩下的字符串,而这时已经没有剩下的了。那么,/w*
规则会不情愿地吐出一个字符,给/d+
匹配。
同时,在吐出字符之前,记录一个点,这个点就是用于回溯的点。然后/d+
匹配“y”,发现不能匹配成功,此时会要求/w*
再吐出一个字符;/w*
先记录一个回溯的点,再吐出一个字符。这时,/w*
匹配结果只有“copy200”,已经吐出“3y”。/d+
再去匹配“3”,发现匹配成功,这时会通知引擎,并且直接显示出来。所以,(/d+)
的结果是 3,而不是 2003。
如果改为非贪婪模式呢?/w*?(/d+)
匹配的结果就应该是 2003。由于/w*?
是非贪婪的,正则引擎会用表达式/w*?
每次仅匹配一个字符串,然后再将控制权交给后面的(/d+)
匹配下一个字符,同时记录一个点,用于匹配不成功时,返回这里再次匹配。
4、谨慎使用点号元字符,尽可能不用星号和加号这样的任意量词
只要能够确定要匹配的范围,就不要使用点号;只要能够确定重复次数,就不要使用星号或加号。
5、优先使用函数
当我们要处理某个字符串时,虽然使用字符串处理函数和正则表达式都可以实现,但两者相比的话,还是字符串处理函数的效率更高。所以在处理字符串的时候可以优先考虑使用字符串处理函数来实现,当字符串处理函数无法实现,或者实现成本过高时,再尝试使用正则表达式。
6、起始、行描点优化
在起始的位置,使用 ^ 能提高匹配的速度。同理,在结尾处使用 $ 标记,正则引擎会从符合长度条件的地方开始匹配,略过目标字符串中的一些字符。在写正则表达式时,应该将描点独立出来,例如“^(?:abc|123)”比“^123|abc”效率要高,而“^(abc)”比“(^abc)”效率更高。
注意:这个原则不适用于所有正则引擎。比如在 PCRE 中,二者效率相当。
7、量词等价转换的效率差异
在 PHP 中,使用/d/d/d
和/d{3}
或者====
和={4}
,他们之间的效率几乎没有差别。但是换用其他语言可能就会有比较明显性能差异了。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/24072.html