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


585

有没有一种快速的方法来查找Ruby中正则表达式的每个匹配项?我浏览了Ruby STL中的Regex对象,并在Google上搜索都没有用。


3
我读到这是我怎样才能在字符串中搜索所有正则表达式模式,并感到非常困惑……
Hugoagogo

Answers:


820

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

string.scan(/regex/)

9
但是,这种情况又如何呢?“匹配我!”。scan(/.../)= [“ mat”,“ ch”“我!” ],但是/.../的所有出现都是[“ mat”,“ atc”,“ tch”,“ ch”,...]
Michael Dickens

13
不是,不是这样。/.../是正常的贪婪正则表达式。它不会在匹配的内容上回溯。您可以尝试使用懒惰的正则表达式,但即使这样也可能不够。看看regexp doc ruby-doc.org/core-1.9.3/Regexp.html正确表达您的regexp :)
Jean Jean

49
这似乎是一个Ruby WTF ...为什么在String而不是Regexp上加上其他regexp东西?Regexp的文档中甚至都没有提及
Anentropic

8
我猜这是因为它是在String上定义和调用的,而不是在Regex上调用的...但是它确实有意义。您可以编写一个正则表达式以使用Regex#match捕获所有匹配项并遍历捕获的组。在这里,您编写了一个部分匹配函数,并希望将它多次应用到给定的字符串上,这不是Regexp的职责。我建议您检查一下扫描的实现以更好地了解:ruby-doc.org/core-1.9.3/String.html#method-i-scan
吉恩

9
@MichaelDickens:在这种情况下,您可以使用/(?=(...))/
Konrad Borowski

67

要查找所有匹配的字符串,请使用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]

如果您想了解更多信息,请参见以下问题:

阅读有关特殊变量$&$'$1$2在Ruby中会有所帮助了。


12

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

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 {$&}
Tin Man

也许你误会了。我回答的用户示例的正则表达式是:/(\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 {$&}
MVP

-1

您可以使用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"]按照其他答案所示可能更安全。
Tin Man
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.