ruby - 不同 Ruby 版本的 `scan` 和 `match` 行为存在差异

标签 ruby regex match

背景

这个问题是关于 String#scan 的行为的和 String#match Ruby 中的方法。我正在使用递归正则表达式,它应该匹配一对平衡的大括号。可以看到这个正则表达式/(\((?:[^\(\)]*\g<0>*)*\))/在行动中: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)"]]

ruby-2.4.1 中的情况是我所期待的。 match在两种情况下都正确匹配相同的外括号组,(x*(3-4)+5) , 但在 ruby​​-2.3.3 中,由于某种原因,第一组比赛只是 ")" .如果我将正则表达式更改为 /(\(.*\))/ ,则两个版本的行为相同(与上面的 2.4.1 相同),但它不再确保嵌套大括号是平衡的。

match 的真实预期行为是什么?在这种情况下?

最佳答案

首先,我应该指出,在 regex101.com 上有效的东西不一定在任何地方都有效:在在线正则表达式测试器的帮助下编写的任何正则表达式必须在目标环境中进行测试。您使用 PCRE 选项进行了测试,它起作用了,因为 PCRE 是一个不同于 Ruby 中使用的 Onigmo 的库。

现在,问题似乎是 Onigmo 正则表达式引擎如何处理 2.3.3 中的递归:\g<0>构造递归整个模式(第 0 组),并且外部捕获括号(第 1 组)也重复(同时其 ID 保持不变),有效地创建了一个重复捕获组。这些组中的值在每次迭代时都会被重写,这就是你得到 ) 的原因最后。

解决方法是递归第 1 组子模式以完整保留第 1 组值,而无需在每次迭代时重写其值(由于模式中定义了捕获组,String#scan 仅返回 capture( s)).

使用

r = /(\((?:[^\(\)]*\g<1>*)*\))/
                      ^

关于ruby - 不同 Ruby 版本的 `scan` 和 `match` 行为存在差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43007936/

相关文章:

php - Mysql PHP 在文本中查找名称

ruby-on-rails - Rails 请求范围内的单例

正则表达式翻转并合并除第一个反斜杠之外的所有反斜杠

javascript正则表达式不匹配一个词

java - Java中基于双点不包括单点的分割

Javascript RegEx恰好一个数字模式

r - 根据其他列设置列的值

ruby - Ruby 中带有 html 标签的 ANSI 转义代码?

ruby-on-rails - 在运行时而不是在 stub 声明中执行 rspec stub ?

MySQL 事件记录写入新创建的表时出现问题