如何获取字符串中所有出现的 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 正则表达式的匹配数据?的主要内容,如果未能解决你的问题,请参考以下文章