我有一个巨大的文件aab.txt
,其内容为aaa
... aab
。
令我惊讶的是
perl -ne '/a*bb/' < aab.txt
比
更快地运行(匹配失败)perl -ne '/a*b/' < aab.txt
(匹配成功)。为什么????两者都应首先吞噬所有的a,然后第二个立即成功,而第一个将不得不一遍又一遍地回溯,失败。
答案 0 :(得分:8)
Perl regexes优化为尽可能早地失败,而不是尽可能快地成功。在浏览大型日志文件时,这很有意义。
优化首先查找字符串的常量部分,在本例中为“浮动”b
或bb
。这可以相当有效地检查,而不必跟踪回溯状态。找不到bb
,匹配就在那里中止。
b
不是这样。找到浮动子字符串,并从那里构造匹配。以下是正则表达式匹配的调试输出(程序为"aaab" =~ /a*b/
):
Compiling REx "a*b"
synthetic stclass "ANYOF_SYNTHETIC[ab][]".
Final program:
1: STAR (4)
2: EXACT <a> (0)
4: EXACT <b> (6)
6: END (0)
floating "b" at 0..2147483647 (checking floating) stclass ANYOF_SYNTHETIC[ab][] minlen 1
Guessing start of match in sv for REx "a*b" against "aaab"
Found floating substr "b" at offset 3...
start_shift: 0 check_at: 3 s: 0 endpos: 4 checked_upto: 0
Does not contradict STCLASS...
Guessed: match at offset 0
Matching REx "a*b" against "aaab"
Matching stclass ANYOF_SYNTHETIC[ab][] against "aaab" (4 bytes)
0 <> <aaab> | 1:STAR(4)
EXACT <a> can match 3 times out of 2147483647...
3 <aaa> <b> | 4: EXACT <b>(6)
4 <aaab> <> | 6: END(0)
Match successful!
Freeing REx: "a*b"
您可以使用debug
pragma的re
选项获得此类输出。
严格来说,查找b
或bb
是不必要的,但它可以让匹配更早失败。
答案 1 :(得分:6)
/a*bb/
基本上是
/^(?s:.*?)a*bb/
注意两个*
。除了优化之外,它是二次的。在最坏的情况下,(所有a
的字符串),对于长度为N的字符串,它将检查当前字符是否为a
N *(N-1)/ 2次。我们称之为O(N 2 )。
在开始匹配之前,有必要扫描字符串(O(N))以查看它是否可能匹配。匹配需要更长的时间,但它将无法更快地匹配。这就是Perl所做的。
运行以下内容时
perl -Mre=debug -e"'aaaaab' =~ /a*bb/"
您可以获得有关模式编译的信息:
Compiling REx "a*bb"
synthetic stclass "ANYOF{i}[ab][{non-utf8-latin1-all}]".
Final program:
1: STAR (4)
2: EXACT <a> (0)
4: EXACT <bb> (6)
6: END (0)
floating "bb" at 0..2147483647 (checking floating) stclass ANYOF{i}[ab][{non-utf8-latin1-all}] minlen 2
最后一行表示在开始匹配之前会在输入中搜索bb
。
您可以获得有关模式评估的信息:
Guessing start of match in sv for REx "a*bb" against "aaaaab"
Did not find floating substr "bb"...
Match rejected by optimizer
在这里,您会看到检查操作。