【问题标题】:Misunderstanding perl regexp evaluation误解 perl 正则表达式评估
【发布时间】:2013-07-20 07:56:02
【问题描述】:

一天的好时光! 我正在读一本关于 perl 的书:Larry Wall、Tom Christiansen、Jon Orwant 的“Programming Perl”。在这本书中,我发现了几个作者没有阐明的例子(或者我当时根本不明白)。

第一个

这只会打印一次 hi。

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

但这会打印两次“hi”??怎么解释?

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

继续尝试甚至让事情变得更糟:

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

上面的代码字符串再次只打印一次这个可怕的“hi”! 大约一周后,我只完全理解了一件事——我需要帮助:) 所以我请你帮助我,拜托。

第二个(这是炸弹!)

 $_ = "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次回滚,当.*一次从其匹配字符返回时(7次)所以匹配一个全模式发生。

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

拜托,你能解释一下我很高兴见到这个奇怪的 perl 代码吗? 感谢您的任何回答!

【问题讨论】:

  • 也许use re 'debug' 可以帮助阐明这一点。
  • 在回溯过程中“撤消”对本地人的更改解释了in the docs,很难总结更多。

标签: regex perl


【解决方案1】:

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

"警告:这个扩展的正则表达式功能被认为是实验性的,可能会在没有通知的情况下进行更改。由于正则表达式引擎中未来优化的影响,执行的具有副作用的代码在不同版本之间的执行可能不同。实现在 5.18.0 版本中对这个特性进行了彻底的修改,它在 perl 早期版本中的行为更加错误,尤其是在解析、词法变量、作用域、递归和重入方面。”

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

【讨论】:

  • 你说的“撤消”或回溯/取消设置/还原变量的行为是否仅与本地变量有关?我的意思是如果我删除运算符local 我真的得到i 等于11。但为什么只有局部变量?为什么还原变量仅与本地有关?运算符local 的哪些特殊属性或特性是决定仅恢复局部变量的关键特性?
  • 说实话我并不完全理解这一点。我不知道为什么非局部变量会有所不同。当我运行没有local 关键字的炸弹时,我得到final 11 而不是final 5。话虽如此,我正在使用 Perl 5.14.2,它现在可能已经很老了。
【解决方案2】:

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

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

这是正则表达式的调试信息:

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

还有我的 cmets 的执行痕迹:

#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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-26
    • 1970-01-01
    相关资源
    最近更新 更多