如何在Ruby中匹配所有出现的正则表达式

izj3ouym  于 2022-11-04  发布在  Ruby
关注(0)|答案(6)|浏览(473)

有没有一种快速的方法可以在Ruby中找到正则表达式的每个匹配项?我已经在Ruby STL中查看了Regex对象,并在Google上搜索了一下,但没有找到。

wvt8vs2t

wvt8vs2t1#

使用scan应该可以做到这一点:

string.scan(/regex/)
jtw3ybtb

jtw3ybtb2#

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

str = "A 54mpl3 string w1th 7 numb3rs scatter36 ar0und"
str.scan(/\d+/)

# => ["54", "3", "1", "7", "3", "36", "0"]

如果您需要MatchData(Regexp match方法传回的对象型别),请用途:

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中的特殊变量$&$'$1$2也会有所帮助。

6vl6ewon

6vl6ewon3#

如果您有一个包含组的正则表达式:

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"]
bejyjqdl

bejyjqdl4#

你可以使用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 }
euoag5mw

euoag5mw5#

如果在正则表达式中有用于其他目的的捕获组(),则建议的String#scanString#match解决方案会有问题:

  1. String#scan只得到capture groups里面的内容;
  2. String#match只得到第一个匹配,拒绝所有其它匹配;
  3. String#matches(建议的函数)获取所有匹配项。
    在这种情况下,我们需要一个解决方案来匹配正则表达式,而不考虑捕获组。

String#matches的名称

使用Refinements,你可以猴子修补String类,实现String#matches,这个方法将在使用精化的类的作用域中可用。这是一个在Ruby上猴子修补类的不可思议的方法。

设置

  • /lib/refinements/string_matches.rb

# This module add a String refinement to enable multiple String#match()s

# 1. `String#scan` only get what is inside the capture groups (inside the parens)

# 2. `String#match` only get the first match

# 3. `String#matches` (proposed function) get all the matches

module StringMatches
  refine String do
    def matches(regex)
      scan(/(?<matching>#{regex})/).flatten
    end
  end
end

已使用:named capture groups

用法

  • rails c
> require 'refinements/string_matches'

> using StringMatches

> 'function(1, 2, 3) + function(4, 5, 6)'.matches(/function\((\d), (\d), (\d)\)/)
=> ["function(1, 2, 3)", "function(4, 5, 6)"]

> 'function(1, 2, 3) + function(4, 5, 6)'.scan(/function\((\d), (\d), (\d)\)/)
=> [["1", "2", "3"], ["4", "5", "6"]]

> 'function(1, 2, 3) + function(4, 5, 6)'.match(/function\((\d), (\d), (\d)\)/)[0]
=> "function(1, 2, 3)"
7lrncoxx

7lrncoxx6#

返回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.new('string', /pattern/).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语句下面。在你include它一次之后,它将在任何地方可用,甚至在你包含它的类之外)。

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]

相关问题