ChatGPT解决这个技术问题 Extra ChatGPT

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

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

我读到这是如何搜索所有正则表达式模式的字符串并且非常困惑......

A
Andrew Marshall

使用 scan 应该可以解决问题:

string.scan(/regex/)

但是这个案子是什么? "匹配我!".scan(/.../) = [ "mat", "ch " "me!" ],但所有出现的 /.../ 将是 [ "mat", "atc", "tch", "ch", ... ]
不会的。 /.../ 是一个正常的贪婪正则表达式。它不会回溯匹配的内容。您可以尝试使用惰性正则表达式,但即使这样也可能还不够。查看正则表达式文档 ruby-doc.org/core-1.9.3/Regexp.html 以正确表达您的正则表达式 :)
这似乎是一个 Ruby WTF ......为什么这是在 String 而不是 Regexp 和其他正则表达式的东西上? Regexp 的文档中甚至都没有提到它
我想这是因为它是在 String 而不是 Regex 上定义和调用的......但它确实有意义。您可以编写一个正则表达式来使用 Regex#match 捕获所有匹配项并遍历捕获的组。在这里,您编写了一个部分匹配函数并希望它在给定字符串上多次应用,这不是 Regexp 的责任。我建议您检查 scan 的实现以便更好地理解:ruby-doc.org/core-1.9.3/String.html#method-i-scan
@MichaelDickens:在这种情况下,您可以使用 /(?=(...))/
t
the Tin Man

要查找所有匹配的字符串,请使用 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 正则表达式的匹配数据?”

“带有命名捕获支持的 Ruby 正则表达式匹配枚举器”

“如何在 ruby 中找出每场比赛的起点”

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


t
the Tin Man

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

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

str.scan(/\d+[m-t]/) # => ["54m", "1t", "3r"]str.to_enum(:scan,re).map {$&} 更惯用
也许你误会了。我回复的用户示例的正则表达式是:/(\d+)[m-t]/ 不是 /\d+[m-t]/ 要写:re = /(\d+)[m-t]/; str.scan(re) 是相同的 str.scan(/(\d+)[mt]/) 但我得到 #> [["" 54 "], [" 1 "], [" 3 "]] 而不是 "54m", "1t", "3r"] 问题是:如果我有一个带有组的正则表达式,并且想在不更改正则表达式(离开组)的情况下捕获所有模式,我该怎么做?从这个意义上说,一个可能的解决方案是:str.to_enum(:scan,re).map {$&}
t
the Tin Man

您可以使用 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"]

正则表达式也可以是一个命名组。

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

您也可以使用 gsub,如果您需要 MatchData,这只是另一种方式。

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

your_regex = /(\d+)[m-t]/ 中删除分组,您就不需要使用 flatten。您的最后一个示例使用 last_match,在这种情况下它可能是安全的,但它是全局的,如果在调用 last_match 之前匹配了任何正则表达式,则可能会被覆盖。相反,根据模式和需求,使用 string.match(regex).captures # => ["group_photo", "jpg"]string.scan(/\d+/) # => ["54", "3", "1", "7", "3", "0"] 可能更安全,如其他答案所示。
V
Victor

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

String#scan 只获取捕获组内的内容; String#match 只获取第一个匹配,拒绝所有其他匹配; String#matches(提议的函数)获取所有匹配项。

在这种情况下,我们需要一个解决方案来匹配正则表达式而不考虑捕获组。

字符串#matches

使用 Refinements,您可以修补 String 类,实现 String#matches,并且此方法将在使用细化的类的范围内可用。这是在 Ruby 上进行 Monkey Patch 类的一种不可思议的方式。

设置

/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

用法

导轨 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)"