有没有一种快速的方法来找到Ruby中正则表达式的每个匹配?我已经查看了Ruby STL中的Regex对象,并在谷歌上进行了搜索,但一无所获。


当前回答

要找到所有匹配的字符串,请使用String的scan方法。

str = "A 54mpl3 string w1th 7 numb3rs scatter36 ar0und"
str.scan(/\d+/)
#=> ["54", "3", "1", "7", "3", "36", "0"]

如果需要,使用Regexp match方法返回的对象类型MatchData:

str.to_enum(:scan, /\d+/).map { Regexp.last_match }
#=> [#<MatchData "54">, #<MatchData "3">, #<MatchData "1">, #<MatchData "7">, #<MatchData "3">, #<MatchData "36">, #<MatchData "0">]

使用MatchData的好处是你可以使用offset这样的方法:

match_datas = str.to_enum(:scan, /\d+/).map { Regexp.last_match }
match_datas[0].offset(0)
#=> [2, 4]
match_datas[1].offset(0)
#=> [7, 8]

如果你想了解更多,请看这些问题:

“如何获得字符串中所有出现Ruby正则表达式的匹配数据?” "支持命名捕获的Ruby正则表达式匹配枚举器" “如何在ruby中找到每个匹配的起点”

阅读Ruby中的特殊变量$&,$',$1,$2也会有帮助。

其他回答

你可以使用string.scan(your_regex).flatten。如果正则表达式包含组,它将以单个普通数组返回。

string = "A 54mpl3 string w1th 7 numbers scatter3r ar0und"
your_regex = /(\d+)[m-t]/
string.scan(your_regex).flatten
=> ["54", "1", "3"]

Regex也可以是命名组。

string = 'group_photo.jpg'
regex = /\A(?<name>.*)\.(?<ext>.*)\z/
string.scan(regex).flatten

你也可以使用gsub,这只是你想要MatchData的另一种方式。

str.gsub(/\d/).map{ Regexp.last_match }

使用scan可以做到这一点:

string.scan(/regex/)

返回一个MatchData对象数组

#scan非常有限——只返回一个简单的字符串数组!

对于我们来说,获得MatchData对象数组更加强大/灵活。

我将提供两种方法(使用相同的逻辑),一种使用PORO,一种使用monkey patch:

孔:

class MatchAll
  def initialize(string, pattern)
    raise ArgumentError, 'must pass a String' unless string.is_a?(String)

    raise ArgumentError, 'must pass a Regexp pattern' unless pattern.is_a?(Regexp)

    @string = string
    @pattern = pattern
    @matches = []
  end

  def match_all
    recursive_match
  end

  private

  def recursive_match(prev_match = nil)
    index = prev_match.nil? ? 0 : prev_match.offset(0)[1]

    matching_item = @string.match(@pattern, index)
    return @matches unless matching_item.present?

    @matches << matching_item
    recursive_match(matching_item)
  end
end

用法:

test_string = 'a green frog jumped on a green lilypad'

MatchAll.new(test_string, /green/).match_all
=> [#<MatchData "green", #<MatchData "green"]

猴子补丁

我通常不会容忍猴子修补,但在这种情况下:

我们将补丁“隔离”到自己的模块中,这是正确的做法 我更喜欢这种方法,因为'string'.match_all(/pattern/)比MatchAll更直观(而且看起来更好)。新('字符串',/模式/).match_all

module RubyCoreExtensions
  module String
    module MatchAll
      def match_all(pattern)
        raise ArgumentError, 'must pass a Regexp pattern' unless pattern.is_a?(Regexp)

        recursive_match(pattern)
      end

      private

      def recursive_match(pattern, matches = [], prev_match = nil)
        index = prev_match.nil? ? 0 : prev_match.offset(0)[1]

        matching_item = self.match(pattern, index)
        return matches unless matching_item.present?

        matches << matching_item
        recursive_match(pattern, matches, matching_item)
      end
    end
  end
end

我建议创建一个新文件,并将补丁(假设您使用Rails)放在/lib/ruby_core_extensions/string/match_all.rb

要使用我们的补丁,我们需要使它可用:

# within application.rb
require './lib/ruby_core_extensions/string/match_all.rb'

然后确保将它包含在String类中(你可以把它放在任何你想要的地方;但是举个例子,就在我们刚刚写的require语句下面。在你包含它一次之后,它将在任何地方可用,甚至在你包含它的类之外)。

String.include RubyCoreExtensions::String::MatchAll

用法:现在当你使用#match_all你会得到这样的结果:

test_string = 'hello foo, what foo are you going to foo today?'

test_string.match_all /foo/
=> [#<MatchData "foo", #<MatchData "foo", #<MatchData "foo"]

test_string.match_all /hello/
=> [#<MatchData "hello"]

test_string.match_all /none/
=> []

我发现当我想匹配多个出现时,这特别有用,然后获得关于每个出现的有用信息,例如出现开始和结束的索引(例如match.offset(0) => [first_index, last_index])

要找到所有匹配的字符串,请使用String的scan方法。

str = "A 54mpl3 string w1th 7 numb3rs scatter36 ar0und"
str.scan(/\d+/)
#=> ["54", "3", "1", "7", "3", "36", "0"]

如果需要,使用Regexp match方法返回的对象类型MatchData:

str.to_enum(:scan, /\d+/).map { Regexp.last_match }
#=> [#<MatchData "54">, #<MatchData "3">, #<MatchData "1">, #<MatchData "7">, #<MatchData "3">, #<MatchData "36">, #<MatchData "0">]

使用MatchData的好处是你可以使用offset这样的方法:

match_datas = str.to_enum(:scan, /\d+/).map { Regexp.last_match }
match_datas[0].offset(0)
#=> [2, 4]
match_datas[1].offset(0)
#=> [7, 8]

如果你想了解更多,请看这些问题:

“如何获得字符串中所有出现Ruby正则表达式的匹配数据?” "支持命名捕获的Ruby正则表达式匹配枚举器" “如何在ruby中找到每个匹配的起点”

阅读Ruby中的特殊变量$&,$',$1,$2也会有帮助。

如果你有一个带组的regexp:

str="A 54mpl3 string w1th 7 numbers scatter3r ar0und"
re=/(\d+)[m-t]/

你可以使用String的scan方法来查找匹配的组:

str.scan re
#> [["54"], ["1"], ["3"]]

找到匹配的模式:

str.to_enum(:scan,re).map {$&}
#> ["54m", "1t", "3r"]