2012-03-02 81 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_"}]