正規表現でマッチした文字列だけすぐほしいとき

結論

マッチデータ全体がほしいとき
/Regexp/=~str&&$&
部分がほしいときには
/Regexp/=~str&&$n

マッチが失敗することを考慮するときにはto_a[1]を投げれば良かったんじゃないか
でも、短いしperlでも使える。よしとしよう。

$ perl -e 'print "abc"=~/./&&$&,"\n"'
a
$ perl -e 'print "abc"=~/d/&&$&,"\n"'

説明

まあ、通常は=~つかってマッチさせて$&と$数字でマッチした文字列を得ると思う。

irb(main):001:0> 'abc'=~/.(.)/
=> 0
irb(main):002:0> $&
=> "ab"
irb(main):003:0> $1
=> "b"

注意:perlは=~が逆だと動かない。

正規表現オブジェクトにmatchメソッドを投げればMatchDataオブジェクトが返ってくるのでそれを利用することもできる。

irb(main):004:0> /.(.)/.match('abc')
=> #<MatchData:0x7ffa1ad4>
irb(main):005:0> /.(.)/.match('abc')[0]
=> "ab"
irb(main):006:0> /.(.)/.match('abc')[1]
=> "b"

ところが、マッチしない場合には[0],[1]はエラーとなってしまう。

irb(main):007:0> /d/.match('abc')[0]
NoMethodError: undefined method `[]' for nil:NilClass
        from (irb):7
        from :0
irb(main):008:0> /d(.)/.match('abc')[1]
NoMethodError: undefined method `[]' for nil:NilClass
        from (irb):8
        from :0

ここで[0]の時には返ってくるMatchDataオブジェクトにto_sを投げればnilにto_sに投げることと同じになるのでいけるのだが、[1]の時にはそうはいかない。

irb(main):009:0> /d/.match('abc').to_s
=> ""
irb(main):010:0> /./.match('abc').to_s
=> "a"

そこで両方に共通な書き方がないかと考えてみたところ、考えついたのが /正規表現/=~マッチ対象&&$& だ。
別段大したことないのだが、記述量が小さいのがいける。(またかよ)

irb(main):011:0> /.(.)/=~'abc'&&$&
=> "ab"
irb(main):012:0> /.(.)/=~'abc'&&$1
=> "b"
irb(main):013:0> /d(.)/=~'abc'&&$&
=> nil
irb(main):014:0> /d(.)/=~'abc'&&$1
=> nil

以上のように match().to_sよりも7文字削減する効果もある。

Arrayオブジェクトにもの申す

irb(main):001:0> [1,2,3][3]
=> nil

何故、例外を起こさない!!*1

と、いうことで例外を起こすようにArray#[]を再定義してみる。

#!/usr/bin/ruby
class Array
  def [](i)
    raise IndexError unless self.size>i.abs
    self.at(i)
  end
end
a=Array[1,2,3]
puts a[0] # =>1
puts a[2] # =>3
puts a[3] # =>IndexError

まあ、Array#atを使った時点で負け犬か(涙

追記11/16 : http://d.hatena.ne.jp/n9d/20071116 へ書き直した

*1:ただ単に 上の記事のto_aが悔しかっただけともいう

例外が怒っても安心して終了するには

例外の書き方

begin
  raise if ...
  ...
  raise "a"
resuce 例外クラス
 ...処理
rescue 例外クラス
 ...処理
rescue
 ....上の例外以外のもの時実行される
else
 ....例外が起きなかったとき実行される
ensure
 ...最期に必ず事項される。(後処理)
end

getsとちがいreadlineはEOF時に例外を出力する。これを利用するとこんなのが書ける。この辺はマニュアルがすごくわかりづらい。

begin
  while true
    puts readline
  end
rescue EOFError
  puts "EOF"
end

例外を複数キャプチャーその1

begin
  while true
    raise "STOP" if readline =~/stop/
    puts $_
  end
rescue EOFError
  puts "EOF"
rescue RuntimeError
  puts $!
end

例外を複数キャプチャーその2

begin
  while true
    raise "STOP" if readline =~/stop/
    puts $_
  end
rescue EOFError
  puts "EOF"
rescue
  puts $!
end

実行結果

$ ./exception.rb 
foo
foo
bar
bar
EOF
$ ./exception.rb 
foo
foo
stop
STOP