一个空的正则表达式在Ruby中匹配什么?

时间:2017-03-07 21:34:20

标签: ruby

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

bash

我知道一个空的正则表达式应该匹配任何字符串,但为什么2.3.3 :015 > 'abc'.scan(//) => ["", "", "", ""] 2.3.3 :016 > 'abc'.match(//) => #<MatchData ""> 在这个例子中返回四个匹配?它是否计算字符串的开头和结尾,然后计算字符之间的每个“空格”?这个结果背后的机制是什么?

1 个答案:

答案 0 :(得分:3)

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

但是让我们回来吧。考虑一下:

"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阅读有关不同正则表达式引擎如何处理零长度匹配的更多信息。