背景
这个问题涉及到Ruby中String#scan
和String#match
方法的行为。我使用的是一个递归正则表达式,它应该与一个平衡的大括号匹配。您可以在:https://regex101.com/r/Q1lOC8/1上看到这个正则表达式https://regex101.com/r/Q1lOC8/1的作用。在这里,它显示了预期的行为:匹配具有平衡的嵌套括号集的顶级括号集。说明此问题的一些示例代码如下:
➜ cat test.rb
s = "1+(x*(3-4)+5)-1"
r = /(\((?:[^\(\)]*\g<0>*)*\))/
puts s.match(r).inspect
puts s.scan(r).inspect
问题
当在ruby-2.3.3和ruby-2.4.1中运行上述示例代码时,我得到了不同的结果:
➜ docker run --rm -v "$PWD":/usr/src/app -w /usr/src/app ruby:2.3.3-alpine ruby test.rb
#<MatchData "(x*(3-4)+5)" 1:")">
[[")"]]
➜ docker run --rm -v "$PWD":/usr/src/app -w /usr/src/app ruby:2.4.1-alpine ruby test.rb
#<MatchData "(x*(3-4)+5)" 1:"(x*(3-4)+5)">
[["(x*(3-4)+5)"]]
红宝石-2.4.1的情况正是我所期待的。在这两种情况下,match
都正确地匹配了相同的括号集,(x*(3-4)+5)
,但是在ruby-2.3.3中,由于某种原因,第一组匹配就是")"
。如果我将正则表达式更改为/(\(.*\))/
,那么这两个版本的行为是相同的(与上面的2.4.1相同),但它将不再确保嵌套大括号是平衡的。
在这种情况下,match
的真正预期行为是什么?
发布于 2017-03-24 20:54:50
首先,我应该注意,在regex101.com上工作的东西不需要在任何地方工作:在在线regex测试器的帮助下编写的任何regex都必须在目标环境中进行测试。您使用PCRE选项进行了测试,并且它成功了,因为PCRE是一个与Ruby中使用的Onigmo不同的库。
现在,问题似乎是Onigmo引擎如何处理2.3.3中的递归:\g<0>
构造递归整个模式(第0组),外部捕获括号(第1组)也被重复(其ID保持不变),从而有效地创建了重复捕获组。这些组中的值在每次迭代时都会重写,这就是为什么最终得到)
的原因。
所做的工作是递归Group 1子模式,使Group 1值保持完整,而无需在每次迭代时重写它的值(因为捕获组是在模式中定义的,所以String#scan
只返回捕获)。
使用
r = /(\((?:[^\(\)]*\g<1>*)*\))/
^
https://stackoverflow.com/questions/43007936
复制相似问题