PHP正则表达式的效率与优化
通过前面的学习我们对正则表达式有了一定的了解,可以尝试自行定义简单的正则表达式了。在定义正则表达式时,尽管需求相同,可能每个人定义的正则表达式或多或少的都会存在一些的不同。虽然都能实现同样的效果,但是它们的执行效率有快有慢,那么该如何定义一个优秀的正则表达式呢?本节来为大家介绍一下。
当使用正则表达式
同时,在吐出字符之前,记录一个点,这个点就是用于回溯的点。然后
如果改为非贪婪模式呢?
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}
,他们之间的效率几乎没有差别。但是换用其他语言可能就会有比较明显性能差异了。所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算