性能 - php 表达式
性能
模式中一些项可能比其他一些更加高效。 比如使用 [aeiou] 这样的字符类会比可选路径 (a|e|i|o|u) 高效。 一般而言, 用尽可能简单的构造描述需求是最高效的。 Jeffrey Friedl 书(精通正则表达式)中包含了很多关于正则表达式性能的讨论。
当一个模式以 .* 开始并且设置了 PCRE_DOTALL 选项时,模式通过PCRE隐式锚定, 因为它可以匹配字符串的开始。然而,如果 PCRE_DOTALL 没有设置, PCRE 不能做这个优化,因为.元字符不能匹配换行符,如果目标字符串包含换行符, 模式可能会从一个换行符后面开始匹配,而不是最开始位置。 比如,模式(.*) second匹配目标字符串 ”first\nand second”(\n 是一个换行符)第一个捕获子组结果是 ”and”。为了这样做, PCRE 尝试从目标字符串中每个换行符后开始匹配。
如果你使用模式匹配没有换行符的目标字符串, 可以通过设置 PCRE_DOTALL 或以 ^.* 开始的模式明确指示锚定以获取最佳性能。 这样节省了 PCRE 沿目标字符串扫描查找换行符重新开始的时间。
小心模式中的无限重复嵌套。这在应用到不匹配字符串时可能会导致运行时间很长。 考虑模式片段(a+)*。
这个模式可以有 33 种方式匹配 ”aaaa”, 并且这个数字会随着字符串的长度的增加迅速增加. (*重复可以匹配0,1,2,3,4次, 并且除了0外每种情况+都有不同次数的匹配对应)。 当模式的剩余部分导致整个匹配失败的时候, PCRE原则上回尝试每种可能的变化, 这将会非常耗时。
对于一些简单的情况的优化是像(a+)*b这样紧接着使用原文字符串.。 在着手正式匹配工作之前,PCRE 检查目标字符串后面是否有 ”b” 字符, 如果没有就立即失败。然而当紧接着没有原文字符的时候这个优化是不可用的。 你可以比较观察(a+)*\d和上面模式的行为差异。 前者在应用到整行的 ”a” 组成的字符串时几乎是立即报告失败, 而后者在目标字符串长于 20 个字符时,时间消耗就相当可观。
与 perl 的不同之处
这里讨论的不同之处是与 perl 5.005相比的。
- 尽管可以使用字符类编译 pcre 进行替代,但仍然默认使用 c 库函数 isspace() 判定一个字符是否是空白字符。通常 isspace() 匹配空格、换页符、换行符、 回车符、水平制表符和垂直制表符。 Perl5 不再将垂直制表符包括到空白字符集中。 \v 这个转义实际上在很长时间都没有得到 perl 文档的承认。然而,这个字符自身 被认为是一个空白字符至少是在 5.002 以上。 在 5.004 和 5.005 它不和 \s 匹配。
- PCRE 不允许前瞻断言的量词修饰,perl 允许这样做,但是这并不是你想象的那样。 例如,(?!a){3}并不意味着接下来 3 个字符不是 a,而是断言下一个字符不是 a 并进行了 3 次断言.
- 捕获子组发生在消极前瞻断言中时被计算在内,但是在偏移向量中并没有设置它们的条目。 Perl 从断言失败之前匹配得到的这些模式匹配结果中设置了它的数值变量(因此是成功的), 但这也仅在消极前瞻断言只有一个分支的时候。
- 尽管目标字符串中支持二进制 0 字符,但是它们在模式字符串中是不允许的,因为它们是通过普通的 c 字符串传递的,而 c 字符串是以 0 字符结束的。转义序列"\x00"可以在模式中表示二进制 0 字符。
- 不支持下面的 perl 转义序列:\l、\u、 \L、 \U。 实际上这些都是通过 perl 一般的字符串处理来实现 的,而不是模式匹配引擎的一部分。
- 不支持 perl 的 \G 断言,因为它与单模式匹配没有关系。
- 很显然,PCRE不支持(?{code}) 和 (??{code})的结构,但是它支持递归模式。
- 在 perl 5.005_2 中档设置为捕获字符串的模式中有部分重复的时候会有一些古怪的现象发生, 比如: /^(a(b)?)+$/ 捕获 aba 的时候, $2 会被设置为 b,然而,如果把模式修改为/^(aa(bb)?)+$/, 用aabbaa去匹配,$2讲不会得到匹配结果。 如果将模式修改为 /^(aa(b(b))?)+$/, $2 和 $3 又都能 得到匹配结果。在 perl 5.004 中 $2 在这几种情况下都能够得到匹配结果,并且 pcre 也是这样。如果 未来 perl 修改为一致那就不同了。pcre 可能接下来会修改.
- 还有一个没有解决的差异是 perl 5.005_02中,模式 /^(a)?(?(1)a|b)+$/ 会匹配字符串 "a", 而 pcre 中不会,然而,perl 和 pcre 中 /^(a)?a/ 匹配 "a" 都会得到相同的结果,$1 都未被设置。
PCRE 提供了一些对 perl 正则表达式的扩展:
- 虽然后瞻断言要求必须匹配固定长度的字符串,然而后瞻断言的每个可选分支 还是可以使用不同长度的字符串的,而perl 5.005 中要求它们必须拥有同样 的长度。
- 如果设置了 PCRE_DOLLAR_ENDONLY 并且没有设置 PCRE_MULTILINE, 元字符 $ 仅仅匹配字符串的末尾(而不是某个换行符之前)。
- 如果设置了 PCRE_EXTRA, 反斜线后紧跟一个没有特殊含义的字符讲会导致错误。
- 如果设置了 PCRE_UNGREEDY, 贪婪量词修饰被逆转,也就是说,默认它们都是非贪婪的了,但如果紧跟一个问号它们就会变成 贪婪的。(译注:既完全将贪婪模式逆转。)
与 POSIX 正则表达式的不同
自 PHP 5.3.0起, POSIX 正则表达式扩展被废弃。在 POSIX 正则和 PCRE 正则之间有一些不同,本页列出了在转向PCRE 时最显著的需要知道的不同点。
- PCRE 函数需要模式以分隔符闭合。
- 不像POSIX,PCRE 扩展没有专门用于大小写不敏感匹配的函数。取而代之的是,支持使用i(PCRE_CASELESS) 模式修饰符完成同样的工作。 其他模式修饰符同样可用于改变匹配策略。
- POSIX 函数从最左面开始寻找最长的匹配,但是 PCRE 在第一个合法匹配后停止。如果字符串 不匹配这没有什么区别,但是如果匹配,两者在结果和速度上都会有差别。 为了说明这个不同, 考虑下面的例子(来自Jeffrey Friedl 的《精通正则表达式》一书)。 使用模式one(self)?(selfsufficient)?在字符串oneselfsufficient上匹配,PCRE 会匹配到oneself,但是使用 POSIX,结果将是整个字符串oneselfsufficient。 两个子串都匹配原始字符串,但是 POSIX 将 最长的作为结果。
POSIX | PCRE |
---|---|
ereg_replace() | preg_replace() |
ereg() | preg_match() |
eregi_replace() | preg_replace() |
eregi() | preg_match() |
split() | preg_split() |
spliti() | preg_split() |
sql_regcase() | 无对等函数 |
In regards to the previous comment that says "there are several other differences including different meaning for the symbols ( [ different rules for which symbols need escaping", as far as I can tell from reading http://www.tin.org/bin/man.cgi?section=7&topic=regex there are absolutely no "other differences" except in what the man page calls "Obsolete ("basic'') regular expressions". However, PHP doesn't appear to use the obsolete form of expressions in its POSIX functions. PCRE even supports all of the POSIX named character classes such as [:space:].
there are several other differences including different meaning for the symbols ( [ different rules for which symbols need escaping (they can't be the same as both standard posix and extended posix) you should read the full documentation for PCRE before chaging any posix regex to use pcre.
鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com
图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!