工藤探偵事務所

Resarch and Investigation

ルビー3分クッキング 第十ニ回「例外処理」の巻

kudo-shunsaku2008-04-23



3分間で覚える Ruby のトピックスです。
でも、本家同様に実際には10分程度掛かります。

大事なのは小さな積み重ねと継続する才能ですね。
一番の怠け者の俺が言うのも変ですけどね。

第十ニ回は、「例外処理」の巻です。

今回は「例外処理」についてお話したいと想います。

例外処理は、自分が書いているプログラム中で問題が発生しそうなところがあれば、その箇所で問題が起きた場合の処理方法をプログラム中に記載することによって、その際の対応を明示することが出来るというものです。

具体的には、例外(Exception)が発生しそうな箇所を、 "begin ... rescure ... end" で囲むことが出来るのです。これにより問題を回避したり、もしくはプログラムを途中で終了させたりなどの対処が自分で指定することが出来ます。

では早速、例外処理の構文を書いてみることにしましょう。

ファイル名は、"rescue_ensure.rb" などとして下さい。

# シチューを煮込みます。
begin
  
  10.times {
      puts "stew."
      sleep 1
  }
  
rescue
  # 火を消します。
  puts "put off."
  exit 1
end

# 出来上がり。
puts "simmer down."

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby rescue_ensure.rb
stew.
stew.
stew.
stew.
stew.
stew.
stew.
stew.
stew.
stew.
simmer down.

C:\rubycode\3minutes2cooking>

10回「煮込み」を繰り返すだけのプログラムですが、その前後を "begin ... rescure ... end" で囲んであります。

ここで "rescue" 以下の部分が例外が発生した場合に、指定出来る箇所です。
もし10回「煮込み」を繰り返す間に例外が発生したら、指定した処理を実行してくれます。

でも今回はどうしても例外が発生しません。
確実に10回「煮込み」をしてくれることでしょう。

では無理矢理にプログラムを中止してみましょう。
もう一度実行してみて、その実行中にコンソール画面から "CTRL+C" と押してみましょう。

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby rescue_ensure.rb
stew.
stew.
stew.
stew.
stew.
rescue_ensure.rb:7:in `sleep': Interrupt
        from rescue_ensure.rb:7
        from rescue_ensure.rb:5:in `times'
        from rescue_ensure.rb:5

C:\rubycode\3minutes2cooking>

実行中に問題があったので(例外ではなくシグナルによる中断ですけど)今度は "rescue" 節を実行してくれるかと思いきや、駄目です。通常通りメッセージが出力されるだけです。つまり、何もしてくれません。

これでは例外処理を書かなくても同様の効果に思えてしまいますが、今回は標準入力から突然に中断の指示を出したので、その時点でプログラムが終了してしまったために、"rescue" 節を実行することなくプログラムが終了してしまったのです。

例外処理の構文では例外の有無に係わらず、必ず最後に処理を実行してくれるようにお願いできる "ensure" 構文があります。

果たして、先ほどの "CTRL+C" で中断した場合にはどうなるんでしょうか?ね。

先ほどのソースコードを変更します。

# シチューを煮込みます。
begin
  
  10.times {
      puts "stew."
      sleep 1
  }
  
rescue
  # 火を消します。
  puts "put off."
  exit 1
ensure 
  # 火を水で消します。
  puts "we quenched the fire with water."
  exit 1
end

# 出来上がり。
puts "simmer down."

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby rescue_ensure.rb
stew.
stew.
stew.
stew.
stew.
we quenched the fire with water.

C:\rubycode\3minutes2cooking>

今度は対処してくれました。
例外処理の構文は、 "begin ... rescure ... ensure ... end" と書くことも出来るのです。
必ず実行して貰いたいときは、"ensure" でお願いしておきましょう。

ちょっと話がずれてきたので、今度はちゃんと例外を発生させて対処する方法を見ていきましょう。

まずは自分で例外を発生させてみましょう。

先ほどのソースコードを変更します。

# 煮込みます。
begin
  
  5.times {
      puts "stew."
      sleep 1
  }

  # 例外を発生。
  raise "boil over!"
  
  5.times {
      puts "stew."
      sleep 1
  }
  
rescue => ex
  # 火を消します。
  puts ex.message
  puts "put off."
  exit 1
ensure 
  # 火を水で消します。
  puts "we quenched the fire with water."
  exit 1
end

# 出来上がり。
puts "simmer down."

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby rescue_ensure.rb
stew.
stew.
stew.
stew.
stew.
boil over!
put off.
we quenched the fire with water.

C:\rubycode\3minutes2cooking>

自分で例外を発生させるには "raise" を使います。

5回「煮込み」を繰り返した後に、例外発生させて、その後また5回「煮込み」を繰り返すことになっています。

実行結果を確認すれば、例外発生後、"rescue" 節を実行しているのが判ります。
"rescue" では、受け取る例外を指定して特定の例外だけを受け取ることも出来ます。
今回は例外を特定はせず、受け取った例外(Exception クラス)を変数に格納しておき、その例外に入っていたメッセージを取り出したりしてますね。

そして最後に "ensure" もちゃんと実行してくれていますね。

ではもう少し現実的な例外処理の例をみましょう。

すぐさま思いつくのは、「ファイルを開く処理を行う」などです。ファイルを開こうとした時にそのファイル自体が存在しなければどうなるのでしょうか?

Ruby の組み込みライブラリにはファイルを操作するものがあります。それを使ってみましょう。

ファイル名は、"file_rescue.rb" などとして下さい。

# ビーフシチューのレシピ
file_name = "beefstew.txt"

begin
  
  open(file_name)
  
rescue => ex
  puts ex.message
  puts "put off."
  exit 1
ensure 
  puts "we quenched the fire with water."
  exit 1
end

# 出来上がり。
puts "simmer down."

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby rescue_ensure.rb
No such file or directory - beefstew.txt
put off.
we quenched the fire with water.

C:\rubycode\3minutes2cooking>

存在しないファイル「ビーフシチューのレシピ」を開こうとしたので、例外が発生しました。
その例外を補足して、処理を行っています。先ほどと同じです。

さらに実践的なものとしては、ファイルを読み書きしている途中で例外が発生した場合などです。こういった処理をプログラムで行う場合は、必ずと言っていいほど例外処理の記述を行っておきましょう。

IO絡みの肝としては、最後に使ったファイルやポートなどはちゃんと閉めて(close)しておかないと開いたファイルが壊れたりしちゃいますからね。ですから、 "ensure" で "close" というのは御作法みたいなものでしょう、きっと。

今回の例題は実際に起こった例外を捕らえるだけでした、次回以降にでもファイルの扱いについてご紹介しますね。


今回は「例外処理」についてお話しました。Java言語では、"try ... catch ... finally" などとしましたが、Ruby言語 では、 "begin ... rescure ... ensure ... end" です。また今回はご紹介してませんがRuby言語では、"catch, throw" は制御文として使いますので注意です。
「例外処理」はスムースにプログラムに処理してもらうためには、必ず必要となってくるので覚えておいた方が得策でしょう。


「スイッチョ♪スイッチョ♪出かけます。♪」

今日はここまで。次回をお楽しみに。