Ruby one-liner用于捕获正则表达式匹配

时间:2014-01-08 15:55:24

标签: ruby regex

在Perl中,我使用以下一行语句通过正则表达式从字符串中提取匹配项并分配它们。这个找到一个匹配并将其分配给一个字符串:

my $string = "the quick brown fox jumps over the lazy dog.";

my $extractString = ($string =~ m{fox (.*?) dog})[0];

结果:$extractString == 'jumps over the lazy'

这个从多个匹配中创建一个数组:

my $string = "the quick brown fox jumps over the lazy dog.";

my @extractArray = $string =~ m{the (.*?) fox .*?the (.*?) dog};

结果:@extractArray == ['quick brown', 'lazy']

是否有相同的方法在Ruby中创建这些单行?

3 个答案:

答案 0 :(得分:8)

使用String#matchMatchData#[]MatchData#captures获得匹配的反向引用。

s = "the quick brown fox jumps over the lazy dog."

s.match(/fox (.*?) dog/)[1]
# => "jumps over the lazy"
s.match(/fox (.*?) dog/).captures
# => ["jumps over the lazy"]

s.match(/the (.*?) fox .*?the (.*?) dog/)[1..2]
# => ["quick brown", "lazy"]
s.match(/the (.*?) fox .*?the (.*?) dog/).captures
# => ["quick brown", "lazy"]

<强>更新

避免undefined method []错误:

(s.match(/fox (.*?) cat/) || [])[1]
# => nil
(s.match(/the (.*?) fox .*?the (.*?) cat/) || [])[1..2]
# => nil
(s.match(/the (.*?) fox .*?the (.*?) cat/) || [])[1..-1] # instead of .captures
# => nil

答案 1 :(得分:6)

string = "the quick brown fox jumps over the lazy dog."

extract_string = string[/fox (.*?) dog/, 1]
# => "jumps over the lazy"

extract_array = string.scan(/the (.*?) fox .*?the (.*?) dog/).first
# => ["quick brown", "lazy"]

如果找不到匹配项,此方法也会返回nil(而不是抛出错误)。

extract_string = string[/MISSING_CAT (.*?) dog/, 1]
# => nil

extract_array = string.scan(/the (.*?) MISSING_CAT .*?the (.*?) dog/).first
# => nil

答案 2 :(得分:3)

首先,在用Ruby编写时要小心用Perl术语思考。为了使代码更具可读性,我们更加冗长地做了一些事情。

我写my @extractArray = $string =~ m{the (.*?) fox .*?the (.*?) dog};为:

string = "the quick brown fox jumps over the lazy dog."

string[/the (.*?) fox .*?the (.*?) dog/]
extract_array = $1, $2
# => ["quick brown", "lazy"]

Ruby和Perl一样,知道capture groups,并将它们分配给值$1$2等。这些使得它在抓取值并分配它们时非常干净和清晰后来。正则表达式引擎也允许您创建和分配命名捕获,但它们往往会模糊正在发生的事情,因此,为了清楚起见,我倾向于这样做。

我们可以使用match来实现目标:

/the (.*?) fox .*?the (.*?) dog/.match(string) # => #<MatchData "the quick brown fox jumps over the lazy dog" 1:"quick brown" 2:"lazy">

但最终结果是否更具可读性?

extract_array = /the (.*?) fox .*?the (.*?) dog/.match(string)[1..-1] 
# => ["quick brown", "lazy"]

命名的捕获也很有趣:

/the (?<quick_brown>.*?) fox .*?the (?<lazy>.*?) dog/ =~ string
quick_brown # => "quick brown"
lazy # => "lazy"

但是他们会想知道这些变量的初始化和分配位置; 肯定不会查找正则表达式,因此它可能会让其他人感到困惑,并再次成为维护问题。


卡里说:

  

详细说明命名捕获,如果match_data = string.match /(?。?)fox。?(?。*?)dog /,则match_data [:quick_brown] #=&gt; “quick brown”和match_data [:lazy]#=&gt; “懒惰”(以及quick_brown#=&gt;“快速褐色”和懒惰#=&gt;“懒惰”)。有了命名的捕获,我认为没有理由使用全局变量或Regexp.last_match等。

是的,但那里也有一些气味。

我们可以将values_at与MatchData结果match一起使用来检索捕获的值,但是类中有一些不直观的行为让我失望:

/the (?<quick_brown>.*?) fox .*?the (?<lazy>.*?) dog/.match(string)['lazy']

有效,并暗示MatchData知道如何表现得像哈希:

{'lazy' => 'dog'}['lazy'] # => "dog"

并且它有一个values_at方法,就像Hash一样,但它不直观地工作:

/the (?<quick_brown>.*?) fox .*?the (?<lazy>.*?) dog/.match(string).values_at('lazy') # => 
# ~> -:6:in `values_at': no implicit conversion of String into Integer (TypeError)

鉴于:

/the (?<quick_brown>.*?) fox .*?the (?<lazy>.*?) dog/.match(string).values_at(2) # => ["lazy"]

现在就像一个数组:

['all captures', 'quick brown', 'lazy'].values_at(2) # => ["lazy"]

我想要保持一致性,这让我头疼。