루비 예외


예외와 실행은 항상 함께 연결됩니다. 존재하지 않는 파일을 열고 상황을 적절하게 처리하지 않으면 프로그램 품질이 낮은 것으로 간주됩니다.

예외가 발생하면 프로그램이 중지됩니다. 예외는 프로그램 실행 중에 발생할 수 있는 다양한 종류의 오류를 처리하기 위해 사용되므로 프로그램을 완전히 중단하지 않고 적절한 조치를 취합니다.

Ruby는 예외 처리를 위한 완벽한 메커니즘을 제공합니다. 예외를 던질 수 있는 코드를 begin/end 블록에 포함하고 rescue 절을 사용하여 Ruby에게 처리할 예외 유형을 알려줄 수 있습니다.

문법

begin #开始
 
 raise.. #抛出异常
 
rescue [ExceptionType = StandardException] #捕获指定类型的异常 缺省值是StandardException
 $! #表示异常信息
 $@ #表示异常出现的代码位置
else #其余异常
 ..
ensure #不管有没有异常,进入该代码块
 
end #结束

begin부터 rescue까지 모든 것이 보호됩니다. 코드 블록 실행 중에 예외가 발생하면 rescueend 사이의 블록으로 제어가 전달됩니다.

begin 블록의 각 rescue 절에 대해 Ruby는 던져진 예외를 각 매개변수와 차례로 비교합니다. 구조 절에 명명된 예외가 현재 발생한 예외와 동일한 유형이거나 해당 예외의 상위 클래스인 경우 일치가 성공합니다.

예외가 지정된 모든 오류 유형과 일치하지 않는 경우 모든 rescue 절 뒤에 else 절을 사용할 수 있습니다.

Example

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
      file = STDIN
end
print file, "==", STDIN, "\n"

위 예시의 출력 결과는 다음과 같습니다. open이 실패하기 때문에 STDINfile을 대체하는 것을 볼 수 있습니다.

#<IO:0xb7d16f84>==#<IO:0xb7d16f84>

retry 문 사용

rescue 블록을 사용하여 예외를 포착한 다음 retry 문을 사용하여 begin 블록의 처음부터 실행을 시작할 수 있습니다.

Syntax

begin
    # 这段代码抛出的异常将被下面的 rescue 子句捕获
rescue
    # 这个块将捕获所有类型的异常
    retry  # 这将把控制移到 begin 的开头
end

Example

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
   fname = "existant_file"
   retry
end

다음은 처리 흐름입니다.

  • 열 때 예외가 발생했습니다.

  • 구조로 건너뛰세요. fname이 재할당됩니다.

  • 재시도를 통해 시작의 시작 부분으로 이동합니다.

  • 이번에는 파일이 성공적으로 열렸습니다.

  • 기본 과정을 계속 진행하세요.

참고: 이름이 변경된 파일이 존재하지 않으면 이 포스의 코드는 무한히 시도됩니다. 따라서 예외를 처리할 때는 retry를 주의해서 사용하세요.

raise 문 사용

raise 문을 사용하여 예외를 던질 수 있습니다. 다음 메서드는 호출 시 예외를 발생시킵니다. 두 번째 메시지가 출력됩니다.

Syntax

raise 

或

raise "Error Message" 

或

raise ExceptionType, "Error Message"

或

raise ExceptionType, "Error Message" condition

첫 번째 형식은 단순히 현재 예외(또는 현재 예외가 없는 경우 RuntimeError)를 다시 발생시킵니다. 이는 예외를 전달하기 전에 해석해야 하는 예외 처리기에서 사용됩니다.

두 번째 양식은 새로운 RuntimeError 예외를 생성하고 해당 메시지를 지정된 문자열로 설정합니다. 그런 다음 예외가 호출 스택에 발생합니다.

세 번째 형태는 첫 번째 매개변수를 사용하여 예외를 생성한 후 두 번째 매개변수로 관련 메시지를 설정합니다.

네 번째 양식은 세 번째 양식과 유사하며, 추가 조건문(예: unless)을 추가하여 예외를 발생시킬 수 있습니다.

Example

#!/usr/bin/ruby

begin  
    puts 'I am before the raise.'  
    raise 'An error has occurred.'  
    puts 'I am after the raise.'  
rescue  
    puts 'I am rescued.'  
end  
puts 'I am after the begin block.'

위 예제의 출력 결과는 다음과 같습니다.

I am before the raise.  
I am rescued.  
I am after the begin block.

raise 사용법을 보여주는 또 다른 예제:

#!/usr/bin/ruby

begin  
  raise 'A test exception.'  
rescue Exception => e  
  puts e.message  
  puts e.backtrace.inspect  
end

위 예제의 출력 결과는 다음과 같습니다.

A test exception.
["main.rb:4"]

ensure 문을 사용하세요

때로는 예외 발생 여부에 관계없이 코드 블록 끝에서 일부 처리가 완료되었는지 확인해야 합니다. 예를 들어 블록에 들어갈 때 파일이 열려 있고 블록을 나갈 때 파일을 닫았는지 확인해야 할 수 있습니다.

이것이

ensure 절의 역할입니다. verify는 마지막 복구 절 뒤에 배치되며 블록이 종료될 때 항상 실행되는 코드 블록을 포함합니다. 블록이 정상적으로 종료되는지, 예외가 발생하여 처리되는지, 또는 포착되지 않은 예외로 종료되는지는 중요하지 않습니다. ensure 블록은 항상 실행됩니다.

Grammar

begin 
   #.. 过程
   #.. 抛出异常
rescue 
   #.. 处理错误 
ensure 
   #.. 最后确保执行
   #.. 这总是会执行
end

Example

begin
  raise 'A test exception.'
rescue Exception => e
  puts e.message
  puts e.backtrace.inspect
ensure
  puts "Ensuring execution"
end

위 예제의 출력 결과는 다음과 같습니다.

A test exception.
["main.rb:4"]
Ensuring execution

else 문을 사용하세요

else 절이 제공되는 경우 일반적으로 rescue 절 뒤에 배치됩니다. 어떤 보장을 하기 전에.

else 절의 본문은 코드 본문이 예외를 발생시키지 않는 경우에만 실행됩니다.

Syntax

begin 
   #.. 过程 
   #.. 抛出异常
rescue 
   #.. 处理错误
else
   #.. 如果没有异常则执行
ensure 
   #.. 最后确保执行
   #.. 这总是会执行
end

Example

begin
 # 抛出 'A test exception.'
 puts "I'm not raising exception"
rescue Exception => e
  puts e.message
  puts e.backtrace.inspect
else
   puts "Congratulations-- no errors!"
ensure
  puts "Ensuring execution"
end

위 예제의 출력 결과는 다음과 같습니다.

I'm not raising exception
Congratulations-- no errors!
Ensuring execution

$! 변수를 사용하여 발생한 오류 메시지를 캡처합니다.

Catch and Throw

raise andrescue의 예외 메커니즘은 오류가 발생하면 실행을 포기할 수 있습니다. 때로는 정상적인 처리 중에 깊게 중첩된 구조에서 뛰어내려야 할 수도 있습니다. 캐치 앤 던지기가 유용한 곳입니다.

catch는 주어진 이름(기호 또는 문자열일 수 있음)을 레이블로 사용하는 블록을 정의합니다. 블록은 던지기(throw)가 발생할 때까지 정상적으로 실행됩니다.

Syntax

throw :lablename
#.. 这不会被执行
catch :lablename do
#.. 在遇到一个 throw 后匹配将被执行的 catch
end

或

throw :lablename condition
#.. 这不会被执行
catch :lablename do
#.. 在遇到一个 throw 后匹配将被执行的 catch
end

Examples

다음 예에서는 사용자가 프롬프트에 대한 응답으로 '!'를 입력하면 사용자와의 상호 작용을 종료하는 데 사용됩니다.

def promptAndGet(prompt)
   print prompt
   res = readline.chomp
   throw :quitRequested if res == "!"
   return res
end

catch :quitRequested do
   name = promptAndGet("Name: ")
   age = promptAndGet("Age: ")
   sex = promptAndGet("Sex: ")
   # ..
   # 处理信息
end
promptAndGet("Name:")

위 프로그램은 사람의 상호작용이 필요하므로 컴퓨터에서 사용해 볼 수 있습니다. 위 예제의 출력 결과는 다음과 같습니다.

Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby

Class Exception

Ruby의 표준 클래스와 모듈은 예외를 발생시킵니다. 모든 예외 클래스는 맨 위에 있는 Exception 클래스를 포함하여 계층 구조를 형성합니다. 다음 수준 아래에는 7가지 유형이 있습니다.

  • Interrupt

  • NoMemoryError

  • SignalException

  • ScriptError

  • StandardError

  • SystemExit

Fatal은 이 기능의 또 다른 기능입니다. 레이어 예외이지만 Ruby 인터프리터는 이를 내부적으로만 사용합니다.

ScriptError와 StandardError에는 모두 일부 하위 클래스가 있지만 여기서는 세부 사항을 알 필요가 없습니다. 가장 중요한 것은 우리 자신의 예외 클래스를 만드는 것입니다. 예외 클래스는 Exception 클래스의 하위 클래스이거나 그 자손이어야 합니다.

예제를 살펴보겠습니다.

class FileSaveError < StandardError
   attr_reader :reason
   def initialize(reason)
      @reason = reason
   end
end

이제 위 예외가 사용되는 다음 예를 살펴보세요.

File.open(path, "w") do |file|
begin
    # 写出数据 ...
rescue
    # 发生错误
    raise FileSaveError.new($!)
end
end

여기에서 가장 중요한 줄은 raise FileSaveError.new($!)입니다. 예외가 발생했음을 나타내기 위해 raise를 호출하고, 특정 예외로 인해 데이터 쓰기에 실패한 FileSaveError의 새 인스턴스에 이를 전달합니다.