误解perl regexp评估

时间:2013-07-20 07:56:02

标签: regex perl

一天中的好时光! 我正在读一本关于perl的书:“编程Perl”由Larry Wall,Tom Christiansen,Jon Orwant撰写。在这本书中,我发现了一些作者没有澄清的例子(或者我当时没有得到)。

第一个

这仅打印ON。

 "adfsfloglig"=~ /.*(?{print "hi"})f/;

但这打印“嗨”两个?怎么解释?

 "adfsfloglig"=~ /.*(?{print "hi"})log/;

继续经历甚至让事情变得更糟:

  "adfsfloglig"=~ /.*(?{print "hi"})sflog/;

上面的代码串再次打印只有这个可怕的“嗨”! 大约一个星期后,我完全理解了一件事 - 我需要帮助:) 所以我请你帮助我。

第二个(这是炸弹!)

 $_ = "lothiernbfj";

 m/        (?{$i = 0; print "setting i to 0\n"})
       (.(?{ local $i = $i + 1; print "\ti is $i"; print "\tWas founded $&\n" }))*
       (?{print "\nchecking rollback\n"})
       er
       (?{ $result = $i; print "\nsetting result\n"})
 /x;
 print "final $result\n";

此处屏幕上最终打印的$result等于.*匹配的字符数,但我不再重复。

当打开调试打印(如上所示)时,我看到,$i每次在$&(字符串的匹配部分)中包含新字符时都会递增$i

最后.*等于11(字符串中的字符数量),然后有7次回滚,当$i一次从其匹配字符串返回时(7次),所以匹配发生了所有模式。

但是,该死的魔法,结果是设置为$result的值!我们并没有在任何地方减少这个价值!所以{{1}}应该等于11!但事实并非如此。作者是对的。我知道。

拜托,你能解释一下这个奇怪的perl代码,我很高兴认识到了吗? 谢谢你的回答!

2 个答案:

答案 0 :(得分:6)

来自http://perldoc.perl.org/perlre.html的文档:

  

“警告:此扩展的正则表达式功能被认为是实验性的,可能会在不事先通知的情况下进行更改。由于正则表达式引擎中未来优化的影响,执行的具有副作用的代码在版本之间可能无法完全相同。这个特性在5.18.0版本中得到彻底改革,它在早期版本的perl中的行为更加困难,尤其是在解析,词法变量,范围,递归和重入等方面。“

即使在失败的匹配中,如果正则表达式引擎达到必须运行代码的程度,它也会运行代码。如果代码仅涉及分配给(本地?)变量以及允许的任何操作,则回溯将导致它撤消操作,因此失败的匹配将不起作用。但print操作无法撤消,结果是您可以从失败的匹配中打印字符串。这就是文档警告不要使用“副作用”嵌入代码的原因。

答案 1 :(得分:5)

我做了一些实验,并将答案作为社区维基,希望人们能够填充它。我试图破解最简单的正则表达式并且不敢处理“炸弹”。

1。 “adfsfloglig”=〜/。*(?{print“hi”})f /;

以下是regexp的调试信息:

Final program:
   1: STAR (3)
   2:   REG_ANY (0)
   3: EVAL (5)
   5: EXACT <f> (7)
   7: END (0)

以及我的评论执行的痕迹:

#matches the whole string with .*
0 <> <adfsflogli>         |  1:STAR(3)
                             REG_ANY can match 11 times out of 2147483647...

#splits the string to <adfs> and <floglig> and prints "hi".
#Why does it split? Not sure, probably, knows about the f after "hi" code
4 <adfs> <floglig>        |  3:  EVAL(5)

#tries to find f in 'floglig' - success
4 <adfs> <floglig>        |  5:  EXACT <f>(7)

#end
5 <adfsf> <loglig>        |  7:  END(0)

2。 “adfsfloglig”=〜/。*(?{print“hi”})log /;

 1: STAR (3)
 2:   REG_ANY (0)
 3: EVAL (5)
 5: EXACT <log> (7)
 7: END (0)

跟踪:

#matches the whole string with .*
0 <> <adfsflogli>         |  1:STAR(3)
                            REG_ANY can match 11 times out of 2147483647...

#splits the string to <adfsflog> and <lig> and prints "hi".
#Probably, it found 'l' symbol after the code block
#and, being greedy, tries to capture up to the last 'l'
8 <adfsflog> <lig>        |  3:  EVAL(5)

#compares the 'lig' with 'log' - failed
8 <adfsflog> <lig>        |  5:  EXACT <log>(7)
                                    failed...

#moves backwards, taking the previous 'l'
#prints 2-nd 'hi'
5 <adfsf> <loglig>        |  3:  EVAL(5)

#compares 'loglig' with 'log' - success
5 <adfsf> <loglig>        |  5:  EXACT <log>(7)

#end
8 <adfsflog> <lig>        |  7:  END(0)

3。 “adfsfloglig”=〜/。*(?{print“hi”})sflog /;

 1: STAR (3)
 2:   REG_ANY (0)
 3: EVAL (5)
 5: EXACT <sflog> (8)
8: END (0)

跟踪:

#matches the whole string with .*
0 <> <adfsflogli>         |  1:STAR(3)
                           REG_ANY can match 11 times out of 2147483647...

#splits the string to <adf> and <sfloglig> and prints "hi".
3 <adf> <sfloglig>        |  3:  EVAL(5)

#compares 'sfloglig' with 'sflog' - success
3 <adf> <sfloglig>        |  5:  EXACT <sflog>(8)

#end
8 <adfsflog> <lig>        |  8:  END(0)