Ruby - 提取正则表达式捕获组的最佳方法是什么?

12

我在阅读一个关于正则表达式组匹配的问题,发现有两种方式可以引用正则表达式中的捕获组:

  1. Match字符串方法,例如 string.match(/(^.*)(:)(.*)/i).captures
  2. 类似Perl风格的捕获组变量,如 $1、$2 等,可以通过 if match =~ /(^.*)(:)(.*)/i 获得
  3. 更新: 正如0xCAFEBABE所提到的,还有第三种选择——last_match方法

哪种更好呢?使用1)时,出于安全考虑,你必须使用if语句来保护不为空值,那么为什么不直接提取信息呢?而使用2)就显得更方便。


2
甚至还有第三个:RegExp.last_match。好吧,世界上总有更多的方法。 - 0xCAFEBABE
没有Match类。你可能指的是MatchData类。 - sawa
@sawa 不好意思,这是一个字符串方法,我已经更新了问题。 - Friedrich 'Fred' Clausen
3个回答

22
自2.4.6版本起,Ruby新增了named_captures功能,可以像这样使用。只需在捕获组内添加?<some_name>语法即可。
/(\w)(\w)/.match("ab").captures # => ["a", "b"]
/(\w)(\w)/.match("ab").named_captures # => {}

/(?<some_name>\w)(\w)/.match("ab").captures # => ["a"]
/(?<some_name>\w)(\w)/.match("ab").named_captures # => {"some_name"=>"a"}

更为重要的是,您可以通过名称引用已命名捕获组!

result = /(?<some_name>\w)(\w)/.match("ab")
result["some_name"] # => "a" 

1
对于现代的 Ruby,大约在 2020 年左右,这是正确的答案。 - moveson
1
您还可以在字符串上调用.match,并传入一个正则表达式,这与此答案相反。 - smoyth

4
对于简单的任务,直接访问伪变量$1等可能更短、更简单,但当事情变得复杂时,通过MatchData实例访问内容几乎是唯一的方法。
例如,假设您正在执行嵌套的gsub
string1.gsub(regex1) do |string2|
  string2.gsub(regex2) do
    ... # Impossible/difficult to refer to match data of outer loop
  end
end

在内部循环中,假设您想引用外部gsub的捕获组。调用$1$2等将不能给出正确的结果,因为通过执行内部gsub循环,最后匹配数据已经发生了变化。这将成为一个错误的源头。

必须通过匹配数据来引用捕获的组:

string1.gsub(regex1) do |string2|
  m1 = $~
  string2.gsub(regex2) do
    m2 = $~
    ... # match data of the outer loop can be accessed via `m1`.
        # match data of the inner loop can be accessed via `m2`.
  end
end

简而言之,如果您想为简单任务执行短暂的hackish操作,您可以使用伪变量。如果您希望保持代码更加结构化和可扩展性,则应通过匹配数据访问数据。

0
从Ruby 3.3.0 开始,可以使用symbolize_names: true选项来对MatchData#named_captures进行命名。
如果关键参数symbolize_names的值为true,那么结果哈希表中的键将会是符号。
m = /(?<a>.)(?<b>.)/.match("01") #=> #<MatchData "01" a:"0" b:"1">

m.named_captures #=> {"a" => "0", "b" => "1"}
m.named_captures(symbolize_names: true) #=> {:a => "0", :b => "1"}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接