以下這個程式,預期使用者要輸入整數:
# encoding: Big5
print "輸入整數:"
input = gets.to_i
puts "#{input} 為 %s" % (input % 2 == 0 ? "偶數" : "奇數")
如果使用者輸入的不是整數,就會出現錯誤:
>ruby main.rb
輸入整數:10
10 為 偶數
>ruby main.rb
輸入整數:ten
main.rb:3:in `Integer': invalid value for Integer(): "ten\n" (ArgumentError)
from main.rb:3:in `<main>'
>
輸入整數:10
10 為 偶數
>ruby main.rb
輸入整數:ten
main.rb:3:in `Integer': invalid value for Integer(): "ten\n" (ArgumentError)
from main.rb:3:in `<main>'
>
在Ruby中程式若發生錯誤,會丟出例外,例外為Exception或其子類別實例,以上例而言就是引發(Raise)ArgumentError物件,如果程式沒有處理例外而丟出至執行環境,則會顯示例外追蹤(Trace back)並中斷程式。如果你想要處理例外,則可以使用begin...rescue語句。例如:
# encoding: Big5
begin
print "輸入整數:"
input = Integer(gets)
puts "#{input} 為 %s" % (input % 2 == 0 ? "偶數" : "奇數")
rescue ArgumentError
puts "請輸入阿拉伯數字"
end
如果使用者輸入錯誤,引發的ArgumentError物件會被rescue比對型態是否相同,如果相同則執行對應的區塊。以上例而言,如果使用者輸入錯誤,就會顯示較友善的提示訊息(而不是丟個使用者看不懂的追蹤訊息):
輸入整數:ten
請輸入阿拉伯數字
請輸入阿拉伯數字
begin..rescue的rescue可以指定多個物件,也可以有多個rescue,如果沒有指定rescue後的物件型態,則表示捕捉所有引發的物件。舉例來說,上例中若使用者於輸入時輸入Ctrl+Z,在Windows環境下會引發TypeError,若輸入Ctrl+C,則會引發Interrupt。下例中處理這些可能的狀況:
# encoding: Big5
begin
print "輸入整數:"
input = Integer(gets)
puts "#{input} 為 %s" % (input % 2 == 0 ? "偶數" : "奇數")
rescue ArgumentError
puts "請輸入阿拉伯數字"
rescue TypeError, Interrupt
puts "使用者中斷程式"
rescue
puts "不明的程式中斷"
end
如果沒有指定rescue後的物件型態,則表示捕捉所有引發的物件,所以這樣的rescue必須置於最後。begin..rescue還可以搭配ensure,一但設置,無論有無引發物件,ensure區塊一定會執行,這通常用來作為關閉若干資源的區塊,例如關閉檔案:
# encoding: Big5
print "檔案名稱:"
name = gets.chomp
file = open(name, "r")
begin
file.each do |line|
print line
end
rescue
print "讀取檔案發生錯誤"
ensure
file.close
end
可以使用raise自行引發例外。例如:
>> begin
?> raise TypeError
>> rescue TypeError
>> print "TypeError"
>> end
TypeError=> nil
>>
?> raise TypeError
>> rescue TypeError
>> print "TypeError"
>> end
TypeError=> nil
>>
可以在rescue捕捉到例外後,將例外物件指定給變數。例如:
>> begin
?> raise TypeError
>> rescue TypeError
>> print e.class
>> end
TypeError=> nil
>>
?> raise TypeError
>> rescue TypeError
>> print e.class
>> end
TypeError=> nil
>>
可以看到,raise後接上的雖然是TypeError類別名稱,但實際上是引發TypeError實例,也就是相當於:
>> begin
?> raise TypeError.new
>> rescue TypeError => e
>> print e.class
>> end
TypeError=> nil
>>
?> raise TypeError.new
>> rescue TypeError => e
>> print e.class
>> end
TypeError=> nil
>>
在使用raise引發錯誤時,可以附上訊息,若rescue捕捉到例外物件時,可以使用message方法取得訊息:
>> begin
?> raise TypeError, "輸入錯誤"
>> rescue TypeError => e
>> print e.message
>> end
輸入錯誤=> nil
>>
?> raise TypeError, "輸入錯誤"
>> rescue TypeError => e
>> print e.message
>> end
輸入錯誤=> nil
>>
這相當於:
>> begin
?> raise TypeError.new("輸入錯誤")
>> rescue TypeError => e
>> print e.message
>> end
輸入錯誤=> nil
>>
?> raise TypeError.new("輸入錯誤")
>> rescue TypeError => e
>> print e.message
>> end
輸入錯誤=> nil
>>
也可以使用例外物件的backtrace取得例外追蹤訊息,訊息會以陣列型態傳回。例如:
# encoding: Big5
begin
print "輸入整數:"
input = Integer(gets)
puts "#{input} 為 %s" % (input % 2 == 0 ? "偶數" : "奇數")
rescue ArgumentError => e
puts "請輸入阿拉伯數字"
print e.backtrace
end
一個執行結果如下:
輸入整數:ten
請輸入阿拉伯數字
["main.rb:4:in `Integer'", "main.rb:4:in `<main>'"]
請輸入阿拉伯數字
["main.rb:4:in `Integer'", "main.rb:4:in `<main>'"]
在定義方法時,若begin...end邊界實際上就是方法邊界,可只撰寫rescue。例如下面這個odd_even?方法:
# encoding: Big5
def odd_even?
begin
print "輸入整數:"
input = Integer(gets)
puts "#{input} 為 %s" % (input % 2 == 0 ? "偶數" : "奇數")
rescue ArgumentError => e
puts "請輸入阿拉伯數字"
print e.backtrace
end
end
odd_even?
可以只寫為:
# encoding: Big5
def odd_even?
print "輸入整數:"
input = Integer(gets)
puts "#{input} 為 %s" % (input % 2 == 0 ? "偶數" : "奇數")
rescue ArgumentError => e
puts "請輸入阿拉伯數字"
print e.backtrace
end
odd_even?