2.3 入出力とエラー対策

(1/1)
1から10の和は55です.
前回作った1から10までの整数の和を求めるプログラムを、見やすく色分け表示するよう改良する。次に、開始値と終了値をキーボードから入力できるようにする。最後に、入力エラー対策を行う。
これは Python に限らずプログラミング言語全般に共通することだが、キーボードから "123" と入力したとき、プログラムは、これを数値(整数)としてとらえるか、数字(文字列)としてとらえるかの違いでエラーが発生し、プログラムが強制終了してしまうことがあるので注意したい。

目次

サンプル・プログラム

計算結果を画面に表示

計算結果を画面に表示 - Python
"output1.py" は、前回作った1から10までの整数の和を求めるプログラムの計算結果を、見やすく色分け表示するように改良したものである。
# 文字色のエスケープシーケンス
COLOR_CYAN   = '\033[36m'	# シアン
COLOR_GREEN  = '\033[32m'	# 緑色
COLOR_YELLOW = '\033[33m'	# 黄色
COLOR_END    = '\033[0m'	# 終了

startInt = 1		# 開始値
goalInt  = 10		# 終了値

# 合計を計算する.
sumInt = 0
for i in range(startInt, goalInt + 1):
    sumInt += i

# 表示文字列を生成する.
dispStr = COLOR_YELLOW + str(startInt) + COLOR_END + 'から' + COLOR_GREEN + str(goalInt) + COLOR_END + 'の和は' + COLOR_CYAN + str(sumInt) + COLOR_END + 'です.'
# 画面に表示する.
print(dispStr)
ターミナル表示では、エスケープシーケンスと呼ばれるコードを使って表示文字色を変更することができる。
たとえば \( abc \) という文字列を緑色で表示したいときは、"\033[32m \( abc \) \033[0m" という文字列を print関数に渡す。"\033[32m" は、その後の文字色を緑色に変更するエスケープシーケンス。"\033[0m" は、文字色変更を元に戻すエスペープシーケンスである。
ここで、表示用文字フォントによっては円マーク がバックスラッシュ になることがあるので、置き換えて読んでほしい。

エスケープシーケンスを含む表示文字列は変数 dispStr に代入する。このとき、計算に使った変数 startInt, goalInt, sumInt は数値なので、str関数を使って文字列に変換し、エスペープシーケンスと結合する。

開始値と終了値をキーボードから入力

開始値と終了値をキーボードから入力 - Python
"input1.py" は、開始値と終了値をキーボードから入力し、合計計算できるように改良した。
# 文字色のエスケープシーケンス
COLOR_CYAN   = '\033[36m'	# シアン
COLOR_GREEN  = '\033[32m'	# 緑色
COLOR_YELLOW = '\033[33m'	# 黄色
COLOR_END    = '\033[0m'	# 終了

# キーボードから入力する(文字列).
startStr = input("開始=")
goalStr  = input("終了=")

# 整数に変換する.
startInt = int(startStr)
goalInt  = int(goalStr)

# 合計を計算する.
sumInt = 0
for i in range(startInt, goalInt + 1):
    sumInt += i

# 表示文字列を生成する.
dispStr = COLOR_YELLOW + startStr + COLOR_END + 'から' + COLOR_GREEN + goalStr + COLOR_END + 'の和は' + COLOR_CYAN + str(sumInt) + COLOR_END + 'です.'
# 画面に表示する.
print(dispStr)
キーボードからの入力は input関数を使う。input関数は、括弧内 (...) の引数を画面に表示し、キーボードから入力した文字列を戻す。数値を入力したら、[Enter]キーを押下する。ここでは、戻ってきた文字列を変数 startStrgoalStr に代入する。
ここで、input関数の戻り値は文字列であることに留意したい。そのままでは数値計算ができないので、int関数を使って、整数に変換する。
入力以外の部分は、"input1.py"と同じだ。

入力エラー対策

入力エラー - Python
"input1.py" で、終了値を "10b" と入力すると、上図のような英語のメッセージを表示し、計算結果を表示しない。"ValueError" と表示していることから、何らかのエラーが発生したことが分かる。

int関数は引数として数値しか扱うことができない。文字(列)である "b" が混在していたため、int関数の内部で処理エラーとなり、10行目でプログラムを強制終了した。以前触れたゼロ除算エラーと同じだ。

ユーザーが入力するプログラムをつくるとき、ユーザーはどんなデータを入力してくるか予想が付かない。そこで、プログラム側で期待する入力値(ここでは数字)かどうかをチェックし、エラー処理を行うのが定石である。これを入力バリデーションと呼ぶ。
ただ、これまで学んできた内容だけでは入力バリデーションを書くことはできないので、今回は、エラーが発生したらメッセージを表示してプログラムを終了するようにすることを目指す。
import atexit
import sys

# 文字色のエスケープシーケンス
COLOR_CYAN   = '\033[36m'	# シアン
COLOR_GREEN  = '\033[32m'	# 緑色
COLOR_YELLOW = '\033[33m'	# 黄色
COLOR_END    = '\033[0m'	# 終了

# キーボードから入力する(文字列).
startStr = input("開始=")
goalStr  = input("終了=")

# 整数に変換する+エラー対策
try:
    startInt = int(startStr)
    goalInt  = int(goalStr)
except Exception as e:
    print("エラー > " + str(e))
    sys.exit()

# 合計を計算する.
sumInt = 0
for i in range(startInt, goalInt + 1):
    sumInt += i

# 表示文字列を生成する.
dispStr = COLOR_YELLOW + str(startStr) + COLOR_END + 'から' + COLOR_GREEN + str(goalStr) + COLOR_END + 'の和は' + COLOR_CYAN + str(sumInt) + COLOR_END + 'です.'
# 画面に表示する.
print(dispStr)
"input2.py" は、int関数でエラーが発生したら、エラーメッセージを画面に表示して終了する仕組みを追加したプログラムである。
"input1.py" との違いは、int関数の前後に try~except というキーワードが加わり、処理がインデントしていることである。

まず、try文からみていくことにしよう。
try文例外処理と呼ばれ、それ以降のインデント部分の中でエラーなどの例外が発生するかどうかを検知する。もし例外が発生したら、セットになっている except節を実行する。except節に続けて、"Exception as e" と書くと、e にエラーメッセージを格納する。
ここでは、int関数で例外が発生したら、print関数を使ってエラーメッセージを表示し、プログラムを終了 sys.exit() する。
なお、sys.exit()メソッドを使う関係で、プログラム冒頭で atexitsysimportする。
入力エラー - Python
try文 の応用として、以前触れた「ゼロ除算」のエラー対策を書いてみてほしい。

練習問題

次回予告

Python には print関数やinput関数といった組み込み関数のほか、三角関数や対数関数といった算術関数といった算術関数が標準で用意されている。また、1から10までの整数の和を求める計算をユーザー定義関数にすることができる。

コラム:ゼロ除算エラー

8086
8086
インテルが1978年(昭和53年)に発表した16ビットCPU「8086」は、インテル製CPUとして初めて除算回路を搭載した。このため、ハードウェアレベルでゼロ除算エラー対策が必要となった。そこで、8086は、ゼロ除算エラーのときにソフトウェア割り込みを発生させる仕組みを内蔵した。
ソフトウェア割り込みはCPU内部で発生するもので、現在実行している処理を一時停止し、あらかじめ指定した割り込み処理を実行する。割り込み処理が終わったら、再び実行していた処理へ制御を戻す。
つまり、Pythonの例外処理と同じことを、45年前の8086アセンブリ言語でやっていたわけである。

割り込み処理はCPUによって仕組みが異なるので一概には言えないが、8086 の場合、メインプログラムに影響を与えないよう、割り込み処理に入ったら、それ以上の割り込みを許可するのかどうかを決め、レジスタをスタックに待避する。多重割り込みを許可する場合は、スタックを利用し、割り込み処理ルーチンを再帰呼び出しする形になる。
これは、非同期処理を連続させるときの一般的な対応方法に似ている。

現在、例外処理や非同期処理と呼ばれている処理の大部分は、アセンブリ言語で書いていた割り込み処理に似ている。
8086の時代には無かったが、パイプライン処理ができるようになったCPUでは、頻繁に割り込みが発生するとパフォーマンスが低下する。これも、非同期処理を連続させることが、リソースを浪費し、場合によってはシステム全体のパフォーマンスに影響を与えることに似ている。
(この項おわり)
header