やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

組込ライブラリ(MatchData)

 正規表現のマッチ情報。

成果物

情報源

MatchData

正規表現のマッチに関する情報を扱うためのクラス。

このクラスのインスタンスは、

などにより得られます。

$~

現在のスコープで最後に成功したマッチに関する MatchDataオブジェクトです。 Regexp.last_match の別名です。

str = '<p><a href="http://example.com">example.com</a></p>'
if %r[<a href="(.*?)">(.*?)</a>] =~ str
  p $~[1]
end
#=> "http://example.com"

メンバ抜粋

インスタンスメソッド

== [] begin captures end eql? hash inspect length 
named_captures names offset post_match pre_match 
regexp size string to_a to_s values_at

to_a

$&, $1, $2,... を格納した配列を返します。

to_a -> [String]
/(foo)(bar)(BAZ)?/ =~ "foobarbaz"
p $~.to_a       # => ["foobar", "foo", "bar", nil]

 $&はマッチしたすべての文字列を結合した文字列。$1は1番目の()にヒットしたもの。以降$2,$3...も同様。

captures

$1, $2, ... を格納した配列を返します。

captures -> [String]

MatchData#to_a と異なり $& を要素に含みません。グループにマッチした部分文字列がなければ対応する要素は nil になります。

/(foo)(bar)(BAZ)?/ =~ "foobarbaz"
p $~.to_a       # => ["foobar", "foo", "bar", nil]
p $~.captures   # => ["foo", "bar", nil]

named_captures

名前付きキャプチャをHashで返します。

named_captures -> Hash

Hashのキーは名前付きキャプチャの名前です。Hashの値はキーの名前に対応した名前付きグループのうち最後にマッチした文字列です。

m = /(?<a>.)(?<b>.)/.match("01")
m.named_captures # => {"a" => "0", "b" => "1"}

m = /(?<a>.)(?<b>.)?/.match("0")
m.named_captures # => {"a" => "0", "b" => nil}

m = /(?<a>.)(?<a>.)/.match("01")
m.named_captures # => {"a" => "1"}

m = /(?<a>x)|(?<a>y)/.match("x")
m.named_captures # => {"a" => "x"}

 capturesとは違って配列でなくハッシュを返す。

[]

self[n] -> String | nil

n 番目の部分文字列を返します。

0 はマッチ全体を意味します。 n の値が負の時には末尾からのインデックスと見倣します(末尾の要素が -1 番目)。n 番目の要素が存在しない時には nil を返します。

/(foo)(bar)(BAZ)?/ =~ "foobarbaz"
p $~.to_a       # => ["foobar", "foo", "bar", nil]
p $~[0]         # => "foobar"
p $~[1]         # => "foo"
p $~[2]         # => "bar"
p $~[3]         # => nil        (マッチしていない)
p $~[4]         # => nil        (範囲外)
p $~[-2]        # => "bar"

 「マッチなし」と「範囲外」がどちらもnilで差別化できない。これが怖い。Rubyは例外にせずnilを返すことが多いな。NULL参照エラー頻発するだろこれ。

self[range] -> [String]

Range オブジェクト range の範囲にある要素からなる部分配列を返します。

/(foo)(bar)/ =~ "foobarbaz"
p $~[0..2]      # => ["foobar", "foo", "bar"]
self[start, length] -> [String]

start 番目から length 個の要素を含む部分配列を返します。

/(foo)(bar)/ =~ "foobarbaz"
p $~[0, 3]      # => ["foobar", "foo", "bar"]
self[name] -> String | nil

name という名前付きグループにマッチした文字列を返します。

/\$(?<dollars>\d+)\.(?<cents>\d+)/.match("$3.67")[:cents] # => "67"
/(?<alpha>[a-zA-Z]+)|(?<num>\d+)/.match("aZq")[:num] # => nil

 存在しない名前ならエラーになる。存在しないインデックスはnilを返すのに、存在しない名前はエラー。統一感のない応答だな。

p /(?<alpha>[a-zA-Z]+)/.match("aZq")[:num] #=> undefined group name reference: num (IndexError)

所感

 ほかにも便利メソッドはあるがここまで。

対象環境

$ uname -a
Linux raspberrypi 5.10.52-v7l+ #1441 SMP Tue Aug 3 18:11:56 BST 2021 armv7l GNU/Linux