如何获取字符串中所有出现的 Ruby 正则表达式的匹配数据?

Posted

技术标签:

【中文标题】如何获取字符串中所有出现的 Ruby 正则表达式的匹配数据?【英文标题】:How do I get the match data for all occurrences of a Ruby regular expression in a string? 【发布时间】:2011-10-11 21:14:09 【问题描述】:

我需要MatchData 用于字符串中每次出现的正则表达式。这与Match All Occurrences of a Regex 中建议的扫描方法不同,因为它只给了我一个字符串数组(我需要完整的 MatchData,以获取开始和结束信息等)。

input = "abc12def34ghijklmno567pqrs"
numbers = /\d+/

numbers.match input # #<MatchData "12"> (only the first match)
input.scan numbers  # ["12", "34", "567"] (all matches, but only the strings)

我怀疑有些方法我忽略了。有什么建议吗?

【问题讨论】:

我想要每场比赛的开始和结束位置。但这与我的问题无关。 MatchData 的存在是有原因的,不是吗?如果我能在第一场比赛中得到它,那么它将对所有比赛都有用。 好的,对于每场比赛,我想要的不止一件东西,放在一个方便的包装里。 在我下面给出的解决方案中,您拥有方便的软件包,正如您所命名的那样(您可以从中获取开始、结束或任何您需要的匹配数据)。还是您正在寻找的其他东西? 【参考方案1】:

我会把它放在这里以便通过搜索使代码可用:

input = "abc12def34ghijklmno567pqrs"
numbers = /\d+/
input.gsub(numbers)  |m| p $~ 

结果符合要求:

⇒ #<MatchData "12">
⇒ #<MatchData "34">
⇒ #<MatchData "567">

有关详细信息,请参阅“input.gsub(numbers) |m| p $~ Matching data in Ruby for all occurrences in a string”。

【讨论】:

感谢您这样做,效果很好,尤其是当我想实际使用 gsub 时。 如果您只想获取 MatchData,请使用scan,而不是这样做。它可以更清晰地传达意图。 @justin,问题明确scan不返回MatchData,而只是一个匹配字符串的数组。 @DeFazer 已经有一段时间了,但是 iirc,$~ 是最后一场比赛的 MatchData,这将使我的评论仍然相关 @Justin,从技术上讲,你是对的。 $~ 确实是最后一场比赛的 MatchData。但是,有一个小技巧 - 因为gsub 在每次迭代中多次设置$~,所以在每次迭代中 |m| p $~ 返回不同的MatchData。此外,我不确定我是否理解scan 在获取MatchData 时有何用处。你能解释一下这部分吗?【参考方案2】:

我目前的解决方案是在 Regexp 中添加一个each_match 方法:

class Regexp
  def each_match(str)
    start = 0
    while matchdata = self.match(str, start)
      yield matchdata
      start = matchdata.end(0)
    end
  end
end

现在我可以做:

numbers.each_match input do |match|
  puts "Found #match[0] at #match.begin(0) until #match.end(0)"
end

告诉我有更好的方法。

【讨论】:

这实际上应该附加到您的原始问题中,除非您打算将其作为答案。 另外,while matchdata = self.match(str, start) 被认为是一个非常难以维护的构造,因为很难知道这是一个错误还是故意的。 为什么要附加到问题中?这是一个答案。我只是希望有一个更好的答案,这就是为什么我不只是接受我自己的答案。如果没有找到更好的答案,那么最终我会将其标记为答案。 请重读我写的内容。附加它除非你打算它是答案。 Stack Overflow 更喜欢将原始海报添加的信息附加到您的原始问题中,但是可以添加 OP 提供的答案作为答案。 ***.com/faq#howtoask 它很干净,易于阅读,而且运行良好。如果你愿意,你可以写一个enumerator。在写我的之前我没有注意到你的答案。它们基本相同。【参考方案3】:

你想要的

"abc12def34ghijklmno567pqrs".to_enum(:scan, /\d+/).map  Regexp.last_match 

给你

[#<MatchData "12">, #<MatchData "34">, #<MatchData "567">] 

如您所见,“技巧”是构建一个枚举器以获取每个last_match

【讨论】:

谢谢。这让我的生活轻松了 10 倍。 这应该在 apidock.com 或类似网站上。你救了我至少 10 根新的白发 :) 令人难以置信的是没有内置的方法,我们不得不求助于这样的黑客。【参考方案4】:

我很惊讶没有人提到 Ruby 标准库中包含的惊人的 StringScanner 类:

require 'strscan'

s = StringScanner.new('abc12def34ghijklmno567pqrs')

while s.skip_until(/\d+/)
  num, offset = s.matched.to_i, [s.pos - s.matched_size, s.pos - 1]

  # ..
end

不,它没有为您提供 MatchData 对象,但它确实为您提供了一个基于索引的字符串接口。

【讨论】:

【参考方案5】:
input = "abc12def34ghijklmno567pqrs"
n = Regexp.new("\\d+")
[n.match(input)].tap  |a| a << n.match(input,a.last().end(0)+1) until a.last().nil? [0..-2]

=> [#<MatchData "12">, #<MatchData "34">, #<MatchData "567">]

【讨论】:

以上是关于如何获取字符串中所有出现的 Ruby 正则表达式的匹配数据?的主要内容,如果未能解决你的问题,请参考以下文章

使用正则表达式从 Ruby 中的字符串中提取子字符串

Ruby高级编程正则

ruby 从正则表达式#ruby搜索字符串中获取匹配的内容

java正则表达式如何获取字符串中所有匹配内容

正则表达式——7.4 单词边界

如何在 ruby​​ 中使用 utf8 的正则表达式