我以为我很了解正则表达式,但这令我感到困惑:
irb(main):016:0> source = "/foo/bar"
=> "/foo/bar"
irb(main):017:0> source.gsub( /[^\/]*\Z/, "fubar" )
=> "/foo/fubarfubar"
据我所知,/[^\/]*\Z/
有一个与bar
匹配的唯一展开式广告,因此应该会生成/foo/fubar
。我完全看不出为什么我会将fubarfubar
作为替代品。
如果我拨打sub
而不是gsub
,则替换有效,所以这不是解决问题的问题,而是揭示我对gsub
的误解。
答案 0 :(得分:5)
您需要使用sub
,因为您只需要在字符串末尾替换一次:
source.sub( /[^\/]*\Z/, "fubar" )
^^^
请参阅IDEONE demo
问题很可能与收集匹配的方式有关,并且由于您模式匹配一个空字符串,尽管最后,最后一个 null 也可以被视为第二个匹配。它不仅是一个Ruby问题,许多其他语言中也存在类似的错误。
所以,实际上,这就是发生的事情:
[^\/]*\Z
模式匹配bar
,并将其替换为foobar
[^\/]*\Z
匹配NULL,并添加另一个foobar
。如果您需要使用gsub
,请替换*
量词,该量词允许匹配0个字符与+
,需要至少出现1次量化子模式,避免匹配0长度字符串:
source.gsub( /[^\/]+\Z/, "fubar" )
^
经验法则:避免在Regex替换方法中匹配空字符串的regexp!
答案 1 :(得分:2)
我根本不认为这是一个错误。正则表达式可以并且将匹配零宽度位置。
因此,正则表达式引擎更像这样看到字符串"xox"
:
"" "x" "" "o" "" "x" ""
(有趣的事实:在Ruby中,上面的实际结果是"xox"
)
如果我们gsub
一个x
_
,那么一切都按预期工作:
"xox".gsub(/x/, "_") #=> "_o_"
但如果我们匹配x*
,事情会变得奇怪:
"xox".gsub(/x*/, "_") #=> "__o__"
这是因为*
匹配零或更多次:
"" "x" "" "o" "" "x" ""
^^^^^^ ^^ ^^^^^^ ^^
如果我们减少零或更多"可能会更清楚。只有零:
"xox".gsub(/x{0}/, "_") #=> "_x_o_x_"
比赛是:
"" "x" "" "o" "" "x" ""
^^ ^^ ^^ ^^
在你的例子中也是如此。您匹配[^\/]
零次或多次。正则表达式引擎匹配字符串末尾的bar
([^\/]
3次)和之后的空白([^\/]
0次):
"/" "" "b" "" "a" "" "r" ""
^^^^^^^^^^^^^^^^^^^^ ^^