最近弄关键词匹配,主要用的正则表达式。之前一直没写过PHP的正则表达式的代码,一番折腾之后表示这其中好多坑。
慎用 |
任务场景是需要在一段字符串中匹配多个关键字。一开始想当然的使用了|
拼接全部的关键字作为pattern,然后直接preg_match
解决问题。
但我这样想当然的做法在Code Review
的时候被拒,理由是可能存在安全等问题。
因为PHP内存限制,pattern过长会造成段异常,而且这样使用性能很低。正确的做法是每一个关键词都是pattern,运行n次preg_match
函数。
1 | // 一次匹配(不推荐) |
在我的测试中,字符串在一万、十万甚至百万长度的时候都是下面多次匹配快于上面的一次匹配,时间差甚至能达到九倍。
想想原因其实很简单,因为上面和下面的逻辑本质上是一样的,但是下面因为一次只匹配一个单词,很自然的能用一些高性能的字符串匹配算法,如KMP。
上面的很长的pattern,反而不好使用各种优化算法,只能用最原始的方法,性能当然会下降。
那|
应该用在什么场合呢?
在拆分开不便于程序代码理解的时候使用。当使用|
可以使代码更加直观简洁,使逻辑更加易于理解,那就放心大胆的用。
当决定要使用正则表达式,其实不太需要考虑性能问题,一般来说性能差别不会很大。
preg_quote 注意添加分隔符
php
提供了preg_match
函数用于转义正则表达式字符,但是需要注意,这个函数仅仅转义正则表达式的特殊字符“ . \ + * ? [ ^ ] $ ( ) { } = ! < > | : -”。
其中并不包含分隔符,调用这个函数的时候最好把使用的分隔符也作为delimiter
参数传递进去。
1 | // '/'是最通用的分隔符。 |
\\\\ 才能在正则表达式中表示 ‘\‘字符
在编写正则表达式反转义函数的时候,我遇到了这个问题。这个函数很简单,就是去掉所有带’\‘字符前的’\‘,但是弄起来总是不对。
在仔细看完PHP文档之后,我终于理清了逻辑。
“\\\\“这是写在代码中的,首先PHP会理解一次,”\\“会表示’\‘,这样传给正则的字符串就是是”\\“。
然后正则表达式再理解一次,这时”\\“对应的就是’\‘字符。在使用’\‘的时候一定要考虑这些情况。
1 | # $keywords 就是需要反转义的字符串 |
善用模式修饰符
php
正则表达式中,分隔符之后是可以添加模式修饰符的,这里可以参阅PHP文档。
我最常用的是u
,此修正符使模式字符串被认为是utf-8的。