Why does capturing named groups in Ruby result in

2020-07-17 08:02发布

I am having trouble with named captures in regular expressions in Ruby 2.0. I have a string variable and an interpolated regular expression:

str = "hello world"
re = /\w+/
/(?<greeting>#{re})/ =~ str
greeting

It raises the following exception:

prova.rb:4:in <main>': undefined local variable or methodgreeting' for main:Object (NameError)
shell returned 1

However, the interpolated expression works without named captures. For example:

/(#{re})/ =~ str
$1
# => "hello"

3条回答
叛逆
2楼-- · 2020-07-17 08:05

As an addendum to both answers in order to make it crystal clear:

str = "hello world"
# => "hello world"
re = /\w+/
# => /\w+/
re2 = /(?<greeting>#{re})/
# => /(?<greeting>(?-mix:\w+))/
md = re2.match str
# => #<MatchData "hello" greeting:"hello">
md[:greeting]
# => "hello"

Interpolation is fine with named captures, just use the MatchData object, most easily returned via match.

查看更多
做自己的国王
3楼-- · 2020-07-17 08:17

Named Captures Must Use Literals

You are encountering some limitations of Ruby's regular expression library. The Regexp#=~ method limits named captures as follows:

  • The assignment does not occur if the regexp is not a literal.
  • A regexp interpolation, #{}, also disables the assignment.
  • The assignment does not occur if the regexp is placed on the right hand side.

You'll need to decide whether you want named captures or interpolation in your regular expressions. You currently cannot have both.

查看更多
做自己的国王
4楼-- · 2020-07-17 08:19

Assign the result of #match; this will be accessible as a hash that allows you to look up your named capture groups:

> matches = "hello world".match(/(?<greeting>\w+)/)
=> #<MatchData "hello" greeting:"hello">
> matches[:greeting]
=> "hello"

Alternately, give #match a block, which will receive the match results:

> "hello world".match(/(?<greeting>\w+)/) {|matches| matches[:greeting] }
=> "hello"
查看更多
登录 后发表回答