我有一个可能很大的文本块来搜索 的实例[[...]]
,其中...
可以是任何东西,包括其他括号(尽管它们不能嵌套; after 的第一个实例]]
结束[[
匹配)。
我可以想出两种方法来匹配这个文本:
- 使用非贪婪限定符:
/\[\[.+?\]\]/
- 使用前瞻:
/\[\[(?:(?!\]\]).)+\]\]/
从性能的角度来看,一个选择本质上是否比另一个更好(我会说第一个可能更具可读性)?我记得读过最好不要使用非贪婪的限定符,但我现在找不到来源。
我有一个可能很大的文本块来搜索 的实例[[...]]
,其中...
可以是任何东西,包括其他括号(尽管它们不能嵌套; after 的第一个实例]]
结束[[
匹配)。
我可以想出两种方法来匹配这个文本:
/\[\[.+?\]\]/
/\[\[(?:(?!\]\]).)+\]\]/
从性能的角度来看,一个选择本质上是否比另一个更好(我会说第一个可能更具可读性)?我记得读过最好不要使用非贪婪的限定符,但我现在找不到来源。
在这种情况下,最好使用非贪婪量词。
拿这个示例字符串"[[a]b]]"
非贪心量词
\[\[.+?\]\] 原子 # 1 2 3 4 5
\[
匹配\[
匹配.+?
匹配"a"
\]
匹配\]
失败,返回 #3 但保留字符串位置.+?
匹配"]"
\]
失败,返回 #3 但保留字符串位置.+?
匹配"b"
\]
匹配\]
匹配展望:
\[\[(?:(?!\]\]).)+\]\] 原子 # 1 2 3 4 5 6 7
\[
匹配\[
匹配(?!\]\])
在 处立即成功(即不匹配)"a"
,继续.
匹配"a"
,在 #3 重复(?!\]\])
在"]"
(?!\]\])
在 处成功(即不匹配)"b"
,继续.
匹配"]"
,在 #3 重复(?!\]\])
在 处立即成功(即不匹配)"b"
,继续.
匹配"b"
,在 #3 重复(?!\]\])
在"]"
(?!\]\])
在 , ergo 处实现完全匹配"]"
:#4 失败,退出 #3\]
匹配\]
匹配所以看起来非贪婪量词要做的工作更少。
免责声明:这是一个人为的示例,实际性能可能会有所不同,具体取决于输入、实际表达式和正则表达式引擎的实现。我只有 98% 的把握,我在这里概述的是实际发生的事情,所以我愿意更正。此外,与所有性能提示一样,不要从表面上看,如果您想确定的话,请进行自己的基准比较。
另一种变体:/\[\[((?:\]?[^]])+)]]/
它既不使用非贪婪量词也不使用前瞻。它允许]
在任何非]
. 如果顺序有两个]
,则内部重复将停止,并且匹配将结束。
这种模式最好与 FSA 编译的正则表达式引擎一起使用。在回溯引擎上,它可能会比非贪婪变体慢。
您使用的是哪种正则表达式风格?如果它支持所有格量词,那么还有一个更好的选择:
\[\[(?:[^\]]++|\](?!\]))*+\]\]
[^\]]++
吞噬除]
使回溯成为可能的状态信息之外的任何字符并且不打扰保存状态信息。如果它确实看到 a ]
,它会执行前瞻以查看是否还有另一个。将整个事物包装在另一个所有格量词中意味着它只在看到 a 时进行前瞻]
,并且只回溯一次:当它找到关闭时]]
。
Java、JGSoft、PCRE (PHP)、Oniguruma (Ruby 1.9) 和 Perl 5.12 支持所有格量词。所有这些风味还支持原子组,可用于实现相同的效果:
\[\[(?>(?:(?>[^\]]+)|\](?!\]))*)\]\]
.NET 风格支持原子组,但不支持所有格量词。
我认为最好使用非贪婪限定符。您确定您阅读的文章不是在说“小心贪婪匹配”吗?