2012-03-02 86 views
10

正如问题标题中所述,是否有一种Ruby字符串的方法等同于String#Scan,但不是仅返回每个匹配的列表,而是返回一个MatchData的数组S'例如:红宝石字符串扫描相当于返回MatchData

# Matches a set of characters between underscore pairs 
"foo _bar_ _baz_ hashbang".some_method(/_[^_]+_/) #=> [#&ltMatchData "_bar_"&rt, &ltMatchData "_baz_"&rt] 

或者任何我能得到相同或相似结果的方式都是好的。我想这样做来查找Ruby字符串中“字符串”的位置和范围,例如在''再见'残酷的'世界'内'"goodbye"world"“。

回答

7

您可以通过使用MatchData#endString#matchpos参数轻松构建您自己的。事情是这样的:

def matches(s, re) 
    start_at = 0 
    matches = [ ] 
    while(m = s.match(re, start_at)) 
     matches.push(m) 
     start_at = m.end(0) 
    end 
    matches 
end 

然后:

>> matches("foo _bar_ _baz_ hashbang", /_[^_]+_/) 
=> [#<MatchData "_bar_">, #<MatchData "_baz_">] 
>> matches("_a_b_c_", /_[^_]+_/) 
=> [#<MatchData "_a_">, #<MatchData "_c_">] 
>> matches("_a_b_c_", /_([^_]+)_/) 
=> [#<MatchData "_a_" 1:"a">, #<MatchData "_c_" 1:"c">] 
>> matches("pancakes", /_[^_]+_/) 
=> [] 

你可以猴子补丁,转换成String,如果你真的想。

+0

太棒了,这正是我所需要的!哈,我正在考虑如何做这样的事情,但我不知道pos param :) – Jwosty 2012-03-02 15:23:46

11
memo = [] 
"foo _bar_ _baz_ hashbang".scan(/_[^_]+_/) { memo << Regexp.last_match } 
=> "foo _bar_ _baz_ hashbang" 
memo 
=> [#<MatchData "_bar_">, #<MatchData "_baz_">] 
+3

+1非常简洁。 'Regexp.last_match'是线程本地的,所以你不会遇到竞争条件。 – Kelvin 2012-10-10 19:21:20

1

如果您不需要获得MatchData的背部,这里是一个使用StringScanner的方式。

require 'strscan' 

rxp = /_[^_]+_/ 
scanner = StringScanner.new "foo _barrrr_ _baz_ hashbang" 
match_infos = [] 
until scanner.eos? 
    scanner.scan_until rxp 
    if scanner.matched? 
    match_infos << { 
     pos: scanner.pre_match.size, 
     length: scanner.matched_size, 
     match: scanner.matched 
    } 
    else 
    break 
    end 
end 

p match_infos 
# [{:pos=>4, :length=>8, :match=>"_barrrr_"}, {:pos=>13, :length=>5, :match=>"_baz_"}]