【问题标题】:What does an empty regexp match in Ruby?Ruby 中的空正则表达式匹配什么?
【发布时间】:2017-03-08 23:57:55
【问题描述】:

我目前正在研究 Ruby 中的正则表达式,有一点我不明白:

2.3.3 :015 > 'abc'.scan(//)
 => ["", "", "", ""] 
2.3.3 :016 > 'abc'.match(//)
 => #<MatchData ""> 

我知道一个空的正则表达式应该匹配任何字符串,但是为什么scan 在这个例子中返回四个匹配?它是否计算字符串的开头和结尾以及字符之间的每个“空格”?这个结果背后的机制是什么?

【问题讨论】:

  • 基本上是因为:'abc' == ''+'a'+''+'b'+''+'c'+'' #=&gt; true

标签: ruby


【解决方案1】:

正则表达式// 不匹配任何字符串;它匹配任何 零长度 字符串。

但是让我们备份。考虑一下:

"ab".scan(/[a-z]/) # => ["a", "b"]

这很容易理解。 String#scan 从索引 0 开始并立即找到匹配项 a,并将其添加到其结果数组中,现在为 ["a"]。然后它将其内部“光标”推进到匹配文本之后的第一个索引,恰好是 1,它再次找到匹配项。现在它的结果是["a", "b"]。最后,它前进到索引 2,它超过了字符串的末尾,所以它停止了。

现在考虑你的例子:

"abc".scan(//)

这几乎是一样的。 String#scan 从索引 0 开始并立即找到匹配项:a 之前的零长度字符串。它将它添加到它的结果数组中,现在是[""]。但现在我们遇到了麻烦。因为匹配的长度是0,所以String#scan在匹配后前进到第一个索引时,仍然是索引0,应该是无限循环吧?

此时,正则表达式引擎做出执行决定。因为前一个匹配的长度为零,所以它不会陷入无限循环,而是将光标前移一个字符。现在索引为 1,然后它找到另一个匹配项:b 之前的零长度字符串。现在它的结果是["", ""]。它再次将光标前移一个字符并在c 之前找到长度为零的字符串,然后在字符串结尾之前(c 之后)再次找到长度为零的字符串。那么它的最终结果就是["", "", "", ""]

这是一个方便的调试习惯用法String#scan

"abc".scan(//) do
  p $~, $~.offset(0)
end

$~Regexp.last_match 的别名,它返回最后一个匹配的MatchData 对象。 $~.offset(0) 返回一个包含匹配开始和结束索引的数组($~.offset(1) 将返回第一个捕获组的开始和结束,依此类推)。在这种情况下,我们得到:

#<MatchData "">
[0, 0]
#<MatchData "">
[1, 1]
#<MatchData "">
[2, 2]
#<MatchData "">
[3, 3]

如您所见,每次匹配一个空字符串("")时,第一个匹配在索引 0 处,最后一个匹配在索引 3 处(“在”字符串中的最后一个字符之后)。在 repl.it 上试用:https://repl.it/GL3N

您可以在(当然)Regular-Expressions.info 阅读有关不同正则表达式引擎如何处理零长度匹配的更多信息。

【讨论】:

  • 非常感谢乔丹,这是一个很棒的解释!我去你链接的网站,看起来很有启发性。
猜你喜欢
  • 1970-01-01
  • 2013-04-19
  • 1970-01-01
  • 2011-07-14
  • 2011-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多