$1
は分かりにくい
正規表現検索でキャプチャーした文字列を取り出すのにこんな書き方をしていた。
def parse_isbn(isbn)
/(\d+)-(\d+)-(\d+)-(\d+)-(\d)/ =~ isbn
{prefix: $1, group: $2, publisher: $3, title: $4, check_digit: $5}
end
p parse_isbn("978-4-7561-3254-3")
#=> {:prefix=>"978", :group=>"4", :publisher=>"7561", :title=>"3254", :check_digit=>"3"}
なんか馬鹿らしい。名前と番号の対応を考えるのがめどい。
ISBN のような,単純かつ一度作ったら変更の無いようなものならいいけど,開発中に正規表現の形がコロコロ変わるようだと,$n
の n
を正しく変更するのが大変だ。
※話を簡単にするため,与えられる ISBN は常に正しいと仮定している。
そうだ名前付きキャプチャーを使おう
Ruby 1.9 になって正規表現エンジンが Oniguruma(鬼車)になり,名前付きキャプチャーが使えるようになった。キャプチャーに名前を付けておいて,対応する部分文字列を番号でなく名前で参照できるというアレだ。ちなみに Ruby 2.0 からは Oniguruma のフォークである Onigmo(鬼雲;u は入らない)に代わっている。
キャプチャーについての詳細はるりまの「正規表現」をどうぞ:
これを使おうではないか。
ただ,名前付きキャプチャーを使うと正規表現が長くなりすぎるきらいがあるので,x
オプションでフリーフォーマットにして書くことにする。
def parse_isbn(isbn)
/(?<prefix> \d+ ) # 接頭記号(978 か 979)
-
(?<group> \d+ ) # グループ記号(国,地域,言語圏)
-
(?<publisher> \d+ )
-
(?<title> \d+ )
-
(?<check_digit> \d )
/x =~ isbn
Regexp.last_match
end
p parse_isbn("978-4-7561-3254-3")
#=> #<MatchData "978-4-7561-3254-3" prefix:"978" group:"4" publisher:"7561" title:"3254" check_digit:"3">
p parse_isbn("978-4-7561-3254-3")[:publisher] #=> "7561"
x
オプションというのは,正規表現リテラル中の空白文字を無視してくれるもの。ご覧の通り #
以降がコメントとみなされる。
でもさ,MatchData じゃなくてキャプチャー文字列のハッシュが欲しいわけよ。
あ,ちなみに MatchData#[]
の引数に名前を与えるとき,名前は String でも Symbol でもいい。
名前付きキャプチャーのハッシュを返すメソッドは無い
絶対そういう目的のメソッドがあるはずと思って調べたけど,どうやら無い。
キャプチャー文字列を配列で返す MatchData#captures
ならある。
名前付きキャプチャーの名前のリストを返す MatchData#names
もある。(シンボルの配列じゃなくて文字列の配列を返すことに注意)
無ければ作れ
こんなでいいかな。
class MatchData
def named_captures
Hash[ names.map{ |name| [name.to_sym, self[name]] } ]
end
end
(2016-08-03 追記)Ruby 2.4.0 でキター!
Ruby 2.4 で MathData#named_captures
が入るようです。
> ruby -v
ruby 2.4.0preview1 (2016-06-20 trunk 55466) [x86_64-linux]
で試しました。
def parse_isbn(isbn)
/(?<prefix> \d+ ) # 接頭記号(978 か 979)
-
(?<group> \d+ ) # グループ記号(国,地域,言語圏)
-
(?<publisher> \d+ )
-
(?<title> \d+ )
-
(?<check_digit> \d )
/x =~ isbn
Regexp.last_match.named_captures
end
parse_isbn "978-4-7561-3254-3"
#=> {"prefix"=>"978", "group"=>"4", "publisher"=>"7561", "title"=>"3254", "check_digit"=>"3"}
やたー!