2012-03-02 19 views
10

Come sostanzialmente affermato nel titolo della domanda, esiste un metodo su stringhe Ruby equivalente a String#Scan ma anziché restituire solo un elenco di ogni corrispondenza, restituirebbe un array di MatchData S? Ad esempio:Ruby String # scan equivalente a restituire MatchData

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

O qualsiasi altro modo per ottenere lo stesso risultato o simile sarebbe positivo. Mi piacerebbe farlo per trovare le posizioni e l'estensione delle "stringhe" all'interno delle stringhe di Ruby, ad es. "goodbye e "world" all'interno di "addio" mondo "crudele" ".

risposta

7

Si potrebbe facilmente creare il proprio sfruttando MatchData#end e il parametro pos di String#match. Qualcosa di simile a questo:

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 

E poi:

>> 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", /_[^_]+_/) 
=> [] 

Si potrebbe patch di scimmia che nella stringa se si voleva davvero.

+0

Impressionante, questo fa esattamente quello che mi serve! Hah, stavo pensando a come avrei potuto fare qualcosa del genere, ma non sapevo del parametro pos :) – Jwosty

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

+1 Molto conciso. E 'Regexp.last_match' è thread-local, quindi non ti imbatterai in condizioni di gara. – Kelvin

1

Se non è necessario per ottenere MatchData s indietro, ecco un modo utilizzando 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_"}]