Ruby Regexp组匹配,在1行上分配变量


125

我目前正在尝试将一个字符串重新表达为多个变量。示例字符串:

ryan_string = "RyanOnRails: This is a test"

我已经将此正则表达式与3组匹配:

ryan_group = ryan_string.scan(/(^.*)(:)(.*)/i)

现在要访问每个组,我必须执行以下操作:

ryan_group[0][0] (first group) RyanOnRails
ryan_group[0][1] (second group) :
ryan_group[0][2] (third group) This is a test

这似乎很荒谬,而且感觉好像我做错了什么。我希望能够做这样的事情:

g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i)

这可能吗?还是有比我更好的方法呢?

Answers:


199

您不希望scan这样做,因为这毫无意义。您可以使用String#matchwhich将返回一个MatchData对象,然后可以调用#captures以返回捕获数组。像这样:

#!/usr/bin/env ruby

string = "RyanOnRails: This is a test"
one, two, three = string.match(/(^.*)(:)(.*)/i).captures

p one   #=> "RyanOnRails"
p two   #=> ":"
p three #=> " This is a test"

请注意,如果未找到匹配项,String#match则将返回nil,因此类似的方法可能会更好:

if match = string.match(/(^.*)(:)(.*)/i)
  one, two, three = match.captures
end

虽然scan这样做没有什么意义。它仍然可以完成工作,您只需要先将返回的Array展平即可。one, two, three = string.scan(/(^.*)(:)(.*)/i).flatten


6
请注意,如果未找到匹配项,则match返回nil,并且您会收到NilError。如果您在Rails中,建议您将以下内容更改 one, two, three = string.match(/(^.*)(:)(.*)/i).captures 为: one, two, three = string.match(/(^.*)(:)(.*)/i).try(:captures)
Andrea Salicetti

5
@AndreaSalicetti我已经编辑了我的帖子,没有在其中添加特定于Rails的代码,所以我使用了用于处理返回的nil对象的版本对其进行了更改
Lee Jarvis 2013年

3
您也可以让新的&.操作员将其恢复到一条线上,甚至在只有一个捕获组时甚至使用两次。例如,string.match(regex)&.captures&.first
Gerry Shaw

46

您可以使用Match或=〜代替,这将为您提供一个匹配项,您可以以相同的方式访问匹配数据,也可以只使用特殊的匹配变量$ 1,$ 2,$ 3

就像是:

if ryan_string =~ /(^.*)(:)(.*)/i
   first = $1
   third = $3
end

5
@Gaston实际上是源自Perl的原始正则表达式语法:)
ohaleck

28

您可以命名捕获的比赛

string = "RyanOnRails: This is a test"
/(?<one>^.*)(?<two>:)(?<three>.*)/i =~ string
puts one, two, three

如果您颠倒了字符串和正则表达式的顺序,则不起作用。


6

您必须确定这是否是一个好主意,但是ruby regexp可以(自动)为您定义局部变量

我尚不确定此功能是很棒还是完全疯狂,但是您的正则表达式可以定义局部变量。

ryan_string = "RyanOnRails: This is a test"
/^(?<webframework>.*)(?<colon>:)(?<rest>)/ =~ ryan_string
# This defined three variables for you. Crazy, but true.
webframework # => "RyanOnRails"
puts "W: #{webframework} , C: #{colon}, R: #{rest}"

(看看http://ruby-doc.org/core-2.1.1/Regexp.html,搜索“局部变量”)。

注意: 正如评论中指出的那样,我看到@toonsend(https://stackoverflow.com/a/21412455)对这个问题有一个类似的较早答案。我不认为我在“偷”东西,但是如果您想对自己的称赞和荣誉感到公平,请放心:)我希望没有动物受到伤害。


这个答案看起来与stackoverflow.com/a/21412455/525478相似,后者已经超过一年了……
Brad Werth,2016年

@BradWerth我想我只是没有看到。但我更新了答案,以包括您的担忧。
Felix

5

scan() 会在您的字符串中找到正则表达式的所有非重叠匹配项,因此它不是返回您似乎期望的组数组,而是返回数组数组。

最好使用match(),然后使用来获取捕获数组MatchData#captures

g1, g2, g3 = ryan_string.match(/(^.*)(:)(.*)/i).captures

但是,scan()如果您想执行以下操作,也可以这样做:

g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i)[0]
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.