Memoize |
BasicWerk
EC Support
Technique
Facebook
|
20140818221222_ruby_regex_grep_match_scan |
ruby_regex_grep_match_scan
% ruby -v ruby 2.0.0p451 (2014-02-24 revision 45167) [universal.x86_64-darwin13]
1.9 以降のバージョンでは grep メソッドは配列に対して動作する。 Shell の grep コマンドが行指向だということを思い出せばなるほどと思う。 # sample.csv > puts `cat sample.csv` id,name,price,currency 1,item-A,2980,JPY 2,item-B,900,JPY 3,item-C,3980,JPY 4,item-D,1980,JPY # 行ごとに配列の要素となるように読み込む > lines = IO.readlines "sample.csv" => ["id,name,price,currency\n", "1,item-A,2980,JPY\n", "2,item-B,900,JPY\n", "3,item-C,3980,JPY\n", "4,item-D,1980,JPY\n"] # 数字から始まっている行だけにマッチさせる(= ヘッダーを取り除く) > lines.grep /^\d/ => ["1,item-A,2980,JPY\n", "2,item-B,900,JPY\n", "3,item-C,3980,JPY\n", "4,item-D,1980,JPY\n"]
Ruby の正規表現リテラルには g フラグがない。 グローバルマッチはメソッド側でコントロールする。 置換であれば単純に sub と gsub の違いである。 # 先にヘッダー以外の行を変数に格納しておこう > rows = lines.grep /^\d/ => ["1,item-A,2980,JPY\n", "2,item-B,900,JPY\n", "3,item-C,3980,JPY\n", "4,item-D,1980,JPY\n"] # 最初の , だけを : に置換 > rows.map {|row| row.sub(/,/, ":")} => ["1:item-A,2980,JPY\n", "2:item-B,900,JPY\n", "3:item-C,3980,JPY\n", "4:item-D,1980,JPY\n"] # 全ての , を | に置換 > rows.map {|row| row.gsub(/,/, "|")} => ["1|item-A|2980|JPY\n", "2|item-B|900|JPY\n", "3|item-C|3980|JPY\n", "4|item-D|1980|JPY\n"]
置換ではなくマッチとキャプチャの組み合わせ(つまりキャプチャしたマッチ文字列だけ抜き出したい)は、 match メソッドが最初にマッチした部分を返し、scan メソッドがマッチした全ての部分文字列を返す。 # 最初に見つけた数字だけ取り出す # まずはキャプチャと $n の組み合わせ > rows.map {|row| row.match(/(\d+)/); $1} => ["1", "2", "3", "4"] # こちらの記法のほうが簡潔 > rows.map {|row| row[/(\d+)/, 1]} => ["1", "2", "3", "4"] # row に含まれる数値部分をすべて取り出す # scan を使った下記の例では、正規表現内のキャプチャは不要(=マッチ文字列全体がキャプチャされる) > rows.map {|row| row.scan /\d+/} => [["1", "2980"], ["2", "900"], ["3", "3980"], ["4", "1980"]] # では scan の正規表現内でキャプチャを使ったらどうなるのか? > rows.map {|row| row.scan /(\d+)/} => [[["1"], ["2980"]], [["2"], ["900"]], [["3"], ["3980"]], [["4"], ["1980"]]] > rows.map {|row| row.scan /(\d+),([^,\n]+)/} => [[["1", "item-A"], ["2980", "JPY"]], [["2", "item-B"], ["900", "JPY"]], [["3", "item-C"], ["3980", "JPY"]], [["4", "item-D"], ["1980", "JPY"]]] # ご覧のとおり、キャプチャ部分を配列にして返してくれる # フラットなリストで欲しい時はその名の通り flatten を使おう > rows.map {|row| row.scan(/(\d+),/).flatten} => [["1", "2980"], ["2", "900"], ["3", "3980"], ["4", "1980"]] > rows.map {|row| row.scan(/(\d+),([^,\n]+)/).flatten} => [["1", "item-A", "2980", "JPY"], ["2", "item-B", "900", "JPY"], ["3", "item-C", "3980", "JPY"], ["4", "item-D", "1980", "JPY"]]
|
© Shin Nakamura/BasicWerk 2014 |