使用交替比使用正则表达式中的后续替换更快

Bra*_*roy 6 regex perl alternation

我有一个很直截了当的问题.在我工作的地方,我看到很多正则表达式都来了.它们在Perl中用于替换和/或删除文本中的一些字符串,例如:

$string=~s/^.+\///;
$string=~s/\.shtml//;
$string=~s/^ph//;
Run Code Online (Sandbox Code Playgroud)

我知道你不能连接第一个和最后一个替换,因为你可能只想ph在第一次替换后在字符串的开头替换.但是,我会将第一个和第二个正则表达式放在一起进行交替:$string=~s/(^.+\/|\.shtml)//;因为我们正在处理数千个文件(+500,000),所以我想知道哪种方法最有效.

Thi*_*Not 10

你的表达不等同

这个:

$string=~s/^.+\///;
$string=~s/\.shtml//;
Run Code Online (Sandbox Code Playgroud)

替换文本.shtml 所有内容,包括最后一个斜杠.

这个:

$string=~s/(^.+\/|\.shtml)//;
Run Code Online (Sandbox Code Playgroud)

取代或者文本.shtml 一切直到并包括最后的斜线.

这是组合正则表达式的一个问题:单个复杂正则表达式比几个简单的正则表达式更难编写,更难理解,更难调试.

哪个更快可能无关紧要

即使你的表达式等效的,使用其中一个可能不会对你的程序速度产生重大影响.像内存中的操作s///明显快于文件I/O,并且你已经表明你正在进行大量的文件I/O.

您应该使用Devel :: NYTProf之类的内容来分析您的应用程序,看看这些特定的替换是否实际上是一个瓶颈(我怀疑它们是否存在).不要浪费时间优化已经很快的事情.

交替阻碍优化器

请记住,您正在比较苹果和橙子,但如果您仍然对性能感到好奇,可以看看perl如何使用repragma评估特定的正则表达式:

$ perl -Mre=debug -e'$_ = "foobar"; s/^.+\///; s/\.shtml//;'
...
Guessing start of match in sv for REx "^.+/" against "foobar"
Did not find floating substr "/"...
Match rejected by optimizer
Guessing start of match in sv for REx "\.shtml" against "foobar"
Did not find anchored substr ".shtml"...
Match rejected by optimizer
Freeing REx: "^.+/"
Freeing REx: "\.shtml"
Run Code Online (Sandbox Code Playgroud)

正则表达式引擎有一个优化器.优化器搜索必须出现在目标字符串中的子字符串; 如果找不到这些子字符串,则匹配会立即失败,而不会检查正则表达式的其他部分.

有了/^.+\//,优化器知道$string必须包含至少一个斜杠才能匹配; 当它找不到斜杠时,它立即拒绝匹配而不调用完整的正则表达式引擎.类似的优化发生在/\.shtml/.

以下是perl对组合正则表达式的作用:

$ perl -Mre=debug -e'$_ = "foobar"; s/(?:^.+\/|\.shtml)//;'
...
Matching REx "(?:^.+/|\.shtml)" against "foobar"
   0 <> <foobar>             |  1:BRANCH(7)
   0 <> <foobar>             |  2:  BOL(3)
   0 <> <foobar>             |  3:  PLUS(5)
                                    REG_ANY can match 6 times out of 2147483647...
                                    failed...
   0 <> <foobar>             |  7:BRANCH(11)
   0 <> <foobar>             |  8:  EXACT <.shtml>(12)
                                    failed...
                                  BRANCH failed...
   1 <f> <oobar>             |  1:BRANCH(7)
   1 <f> <oobar>             |  2:  BOL(3)
                                    failed...
   1 <f> <oobar>             |  7:BRANCH(11)
   1 <f> <oobar>             |  8:  EXACT <.shtml>(12)
                                    failed...
                                  BRANCH failed...
   2 <fo> <obar>             |  1:BRANCH(7)
   2 <fo> <obar>             |  2:  BOL(3)
                                    failed...
   2 <fo> <obar>             |  7:BRANCH(11)
   2 <fo> <obar>             |  8:  EXACT <.shtml>(12)
                                    failed...
                                  BRANCH failed...
   3 <foo> <bar>             |  1:BRANCH(7)
   3 <foo> <bar>             |  2:  BOL(3)
                                    failed...
   3 <foo> <bar>             |  7:BRANCH(11)
   3 <foo> <bar>             |  8:  EXACT <.shtml>(12)
                                    failed...
                                  BRANCH failed...
   4 <foob> <ar>             |  1:BRANCH(7)
   4 <foob> <ar>             |  2:  BOL(3)
                                    failed...
   4 <foob> <ar>             |  7:BRANCH(11)
   4 <foob> <ar>             |  8:  EXACT <.shtml>(12)
                                    failed...
                                  BRANCH failed...
   5 <fooba> <r>             |  1:BRANCH(7)
   5 <fooba> <r>             |  2:  BOL(3)
                                    failed...
   5 <fooba> <r>             |  7:BRANCH(11)
   5 <fooba> <r>             |  8:  EXACT <.shtml>(12)
                                    failed...
                                  BRANCH failed...
Match failed
Freeing REx: "(?:^.+/|\.shtml)"
Run Code Online (Sandbox Code Playgroud)

注意输出多长时间.由于交替,优化器不会启动并执行完整的正则表达式引擎.在最坏的情况下(没有匹配),交替的每个部分都针对字符串中的每个字符进行测试.这不是很有效.

所以,轮换更慢,对吧?没有为什么...

这取决于您的数据

我们再次比较苹果和橘子,但是:

$string = 'a/really_long_string';
Run Code Online (Sandbox Code Playgroud)

组合的正则表达式实际上可能更快,因为s/\.shtml//优化器必须在拒绝匹配之前扫描大部分字符串,而组合的正则表达式快速匹配.

你可以对此进行基准测试以获得乐趣,但由于你在比较不同的东西,它基本上没有意义.