Python ファイルの例外処理
Pythonでファイルを扱う際、ファイルのオープンとクローズは基礎中の基礎です。しかし、これらの操作やデータの読み書きを行う過程で、エラーに遭遇することがあります。これらのエラーは、ファイルが存在しない、アクセス権限がない、あるいは他のプログラムがファイルを使用中であるといった、様々な要因によって引き起こされます。
これらの潜在的なエラーをエレガントに処理し、プログラムのクラッシュを防止するために、Pythonでは try と except ブロックを使用します。本章では、ファイル操作における効果的なエラーハンドリングの手法を網羅的に解説します。
1. try と except ブロックの理解
Pythonにおける try と except ブロックは、例外(Exceptions)、すなわちプログラム実行中に発生するエラーを処理するために使用されます。
基本的な構造は、例外が発生する可能性のあるコードを try ブロックの中に配置します。もし try ブロック内で例外が発生した場合、プログラムは即座に対応する例外タイプの except ブロックへとジャンプします。例外が発生しなかった場合は、except ブロックはスキップされます。
1.1 基本構文 (Syntax)
try と except ブロックの基本構造は以下の通りです。
try:
# 例外が発生する可能性のあるコード
# 例:ファイル操作など
except SomeExceptionType: # 具体的な例外タイプに置き換えます
# 例外を処理するコード
# 例:エラーメッセージの表示や補修措置1.2 実行例:FileNotFoundError の処理
ファイル操作において最も一般的なエラーの一つが FileNotFoundError(ファイル未検出エラー)です。これは存在しないファイルをオープンしようとしたときに発生します。
try:
file = open("nonexistent_file.txt", "r") # 存在しないファイルをオープンしようとする
content = file.read()
print(content)
file.close()
except FileNotFoundError:
print("エラー:ファイル 'nonexistent_file.txt' が見つかりませんでした。")
except Exception as e:
print(f"予期せぬエラーが発生しました:{e}") # 汎用的な例外処理この例では、ファイルが存在しない場合に open() 関数が FileNotFoundError をスローし、プログラムは except FileNotFoundError: ブロックを実行して明確なエラーメッセージを表示します。それ以外のエラーが発生した場合は、2番目の except ブロック(Exception as e)がキャッチします。
2. 複数の例外(Exception)の処理
複数の except ブロックを含めることで、単一の try 構造の中で多種多様な例外を処理できます。各 except ブロックは特定のタイプのエラーをキャッチするように設計できます。
try:
file = open("my_file.txt", "r")
content = file.read()
number = int(content) # 内容を整数に変換(ValueError の可能性)
result = 10 / number # 数値で除算(ZeroDivisionError の可能性)
print(result)
file.close()
except FileNotFoundError:
print("エラー:ファイルが見つかりません。")
except ValueError:
print("エラー:ファイル内のデータが無効です。数値に変換できません。")
except ZeroDivisionError:
print("エラー:ゼロで除算することはできません。")
except Exception as e:
print(f"予期せぬエラーが発生しました:{e}")この例の動作:
- ファイルが存在しない場合は
FileNotFoundErrorを処理。 - 内容が整数に変換できない場合は
ValueErrorを処理。 - 変換後の数値がゼロの場合は
ZeroDivisionErrorを処理。 - 最後の
exceptブロックで、それ以外の想定外の例外をキャッチ。
3. else ブロック
すべての except ブロックの後に else ブロックを追加することもできます。else ブロック内のコードは、try ブロック内で例外が発生しなかった場合のみ実行されます。
try:
file = open("my_file.txt", "r")
content = file.read()
except FileNotFoundError:
print("エラー:ファイルが見つかりません。")
else:
print("ファイルのオープンに成功しました。")
file.close()ここでは、ファイルが正常にオープンされた(例外が発生しなかった)場合のみ、「ファイルのオープンに成功しました。」と表示され、クローズ操作が行われます。
4. finally ブロック
finally ブロックは、例外の発生有無にかかわらず、必ず実行しなければならないコードを指定するために使用されます。これは通常、ファイルの強制的なクローズなど、リソースのクリーンアップに利用されます。
file = None # try ブロックの外で変数 file を初期化
try:
file = open("my_file.txt", "r")
content = file.read()
print(content)
except FileNotFoundError:
print("エラー:ファイルが見つかりません。")
finally:
if file:
file.close()
print("ファイルをクローズしました。")
else:
print("ファイルはオープンされませんでした。")この例では、finally ブロックによってファイルが正常に開かれた場合でも、FileNotFoundError に遭遇した場合でも、確実に事後処理が行われます。変数を try 外で None 初期化することで、finally ブロックから安全にアクセスできるようにしています。
5. 実践ケースとデモンストレーション
5.1 ケース 1:エラー処理を伴うファイル書き込み
書き込み権限の不足やディスク容量不足など、ファイル書き込み時に発生しうるエラーを処理する例です。
try:
file = open("output.txt", "w")
file.write("ファイルに書き込むデータです。\n")
file.write("もう一行のデータ。\n")
except IOError as e:
print(f"エラー:ファイルに書き込めません。{e}")
finally:
if file:
file.close()
print("ファイルをクローズしました。")ここでは、入力/出力エラーを包括する IOError をキャッチしています。権限エラーなどが起きた際も、finally によってリソースが解放されます。
5.2 ケース 2:ファイルの読み込みとデータ処理
ファイルから数値を読み取って合計を計算するシナリオです。FileNotFoundError と、非数値データが含まれる場合の ValueError の両方に対処します。
def calculate_sum_from_file(filename):
total = 0
try:
file = open(filename, "r")
for line in file:
try:
number = float(line.strip()) # 各行を浮動小数点数に変換
total += number
except ValueError:
print(f"警告:無効なデータをスキップします:{line.strip()}")
return total
except FileNotFoundError:
print(f"エラー:ファイル '{filename}' が見つかりませんでした。")
return None
finally:
if 'file' in locals() and file: # file が定義されオープンされているか確認
file.close()
print("ファイルをクローズしました。")
# 実行例
filename = "numbers.txt"
with open(filename, "w") as f: # テスト用データの作成
f.write("1\n")
f.write("2\n")
f.write("abc\n") # 意図的な無効データ
f.write("4\n")
sum_of_numbers = calculate_sum_from_file(filename)
if sum_of_numbers is not None:
print(f"ファイル内の数値の合計:{sum_of_numbers}")内部の try...except で ValueError を処理することで、一部に無効なデータがあっても処理を中断せず、残りのデータを継続して処理できる柔軟な設計になっています。
5.3 ケース 3:with ステートメントによるリソースの自動管理
Python の with ステートメントを使用すると、例外が発生した場合でもリソースが自動的にクローズされるため、明示的な try...finally を書く必要がなくなり、非常にスマートです。
def read_file_content(filename):
try:
with open(filename, 'r') as file:
content = file.read()
return content
except FileNotFoundError:
print(f"エラー:ファイル '{filename}' が見つかりませんでした。")
return None
except IOError as e:
print(f"ファイルの読み込み中にエラーが発生しました:{e}")
return None
# 実行例
filename = "example.txt"
with open(filename, "w") as f:
f.write("こんにちは、世界!\n")
content = read_file_content(filename)
if content:
print("ファイルの内容:\n", content)with open(...) as file: を使うことで、ブロックを抜ける際に自動的にクローズ処理が走ります。これによりコードがシンプルになり、リソースリーク(解放漏れ)のリスクを最小限に抑えることができます。