Python | 1 日 120 分 × 7 日アプリ学習:エラーハンドリング付き入力アプリ(中級編)

Web APP Python
スポンサーリンク

3日目のゴール

3日目のテーマは
「try / except を“入力だけ”で終わらせず、アプリ全体の安全装置として使う」 ことです。

1日目:単発の入力を try / except で守れるようになった。
2日目:安全な入力関数として再利用できる形にした。

3日目では一歩進んで、

ファイル読み書きに try / except を使う
メニュー選択のミスを落ちないようにする
「どこで try を使うか」を意識して設計する

という、“アプリ全体を守る視点” を身につけます。


「入力以外でもエラーは起きる」という感覚を持つ

典型的に落ちる場所を整理する

ここまでの2日間で、
「ユーザー入力 → 型変換」のところに
ValueError が起きることは体感できたと思います。

でも、アプリが落ちる場所はそこだけではありません。

ファイルを開くとき(存在しない、権限がない)
JSONを読み込むとき(壊れている)
0で割るとき
存在しないキーやインデックスにアクセスするとき

つまり、「外部と関わる場所」や「境界」
エラーはよく起きます。

3日目は、その中でも特に大事な
「ファイル」と「メニュー入力」を題材にします。


ファイル読み込みに try / except を使う

まずは「危険な」ファイル読み込み

次のようなコードを考えます。

filename = "data.txt"

with open(filename, "r", encoding="utf-8") as f:
    text = f.read()

print("ファイルの中身:")
print(text)
Python

このコードは、
data.txt が存在していれば問題なく動きます。

でも、ファイルがなかったり、
権限がなかったりすると、こうなります。

FileNotFoundError: [Errno 2] No such file or directory: 'data.txt'
Python

プログラムはそこで終了です。


try / except で「落ちない読み込み」にする

ここに try / except を入れてみます。

filename = "data.txt"

try:
    with open(filename, "r", encoding="utf-8") as f:
        text = f.read()
except FileNotFoundError:
    print(f"{filename} が見つかりませんでした。")
    text = ""
except PermissionError:
    print(f"{filename} を読む権限がありません。")
    text = ""
else:
    print("ファイルを正常に読み込みました。")

print("ファイルの中身:")
print(text)
Python

ここでの重要ポイントを深掘りします。

一つ目は、
「どの例外を捕まえるかを具体的に書いている」 ことです。
FileNotFoundErrorPermissionError を分けることで、
ユーザーに伝えるメッセージも変えられます。

二つ目は、
except の中で text = "" としていることです。
これにより、「読み込みに失敗したけど、空として扱う」という
“アプリとしての方針” を決めています。

三つ目は、else を使って
「エラーがなかったときだけメッセージを出す」
という書き分けをしていることです。
try の中が成功したかどうかが、視覚的に分かりやすくなります。


ファイル読み込みを関数にして「安全な入り口」にする

アプリの外とつながるところを1か所に集約する

ファイル読み込みは、
アプリと外の世界(ファイルシステム)との境界です。

ここに try / except を集中させると、
アプリ全体がぐっと安定します。

def load_text_file(filename):
    try:
        with open(filename, "r", encoding="utf-8") as f:
            text = f.read()
    except FileNotFoundError:
        print(f"{filename} が見つかりませんでした。空のデータとして扱います。")
        return ""
    except PermissionError:
        print(f"{filename} を読む権限がありません。空のデータとして扱います。")
        return ""
    else:
        return text
Python

使う側は、こう書くだけです。

text = load_text_file("data.txt")
print("読み込んだ内容:")
print(text)
Python

ここでの本質は、

「ファイル読み込みの try / except を、アプリのあちこちに書かない」
「外部との境界に“安全な入り口”を1つ作る」

という設計の考え方です。


メニュー入力にもエラーハンドリングを入れる

ありがちな「メニューで落ちる」パターン

よくあるメニューコードは、こんな感じです。

print("1: 足し算")
print("2: 引き算")
print("0: 終了")

choice = int(input("番号を選んでください: "))

if choice == 1:
    print("足し算をします。")
elif choice == 2:
    print("引き算をします。")
elif choice == 0:
    print("終了します。")
else:
    print("不正な番号です。")
Python

ここで「a」などを入力すると、
int("a")ValueError が出て、アプリが落ちます。


メニュー入力を「安全な整数入力」で守る

2日目で作った「安全な整数入力関数」を
メニューにも使ってみます。

def input_menu_number(prompt):
    while True:
        text = input(prompt)

        try:
            value = int(text)
        except ValueError:
            print("数字で入力してください。\n")
            continue

        return value
Python

これを使ってメニューを書くと、こうなります。

def main():
    while True:
        print("1: 足し算")
        print("2: 引き算")
        print("0: 終了")

        choice = input_menu_number("番号を選んでください: ")

        if choice == 1:
            print("足し算をします。\n")
        elif choice == 2:
            print("引き算をします。\n")
        elif choice == 0:
            print("終了します。")
            break
        else:
            print("その番号のメニューはありません。\n")

main()
Python

ここでのポイントは、

「数字以外で落ちる」問題は try / except で防ぎ、
「範囲外の数字」は if / else で扱っている

という分け方です。

try / except はあくまで
「型変換に失敗したとき」のためのもの。
「値として妥当かどうか」は、
通常の条件分岐でチェックする方が読みやすくなります。


「どこまで try に入れるか」を意識する

なんでもかんでも try に入れると、逆に読みにくい

悪い例をあえて出します。

try:
    choice = int(input("番号を選んでください: "))
    if choice == 1:
        print("足し算をします。")
    elif choice == 2:
        print("引き算をします。")
    elif choice == 0:
        print("終了します。")
    else:
        print("その番号のメニューはありません。")
except ValueError:
    print("数字で入力してください。")
Python

一見スッキリしているように見えますが、
try の中に「本来エラーと関係ない処理」まで入っています。

3日目で身につけてほしい感覚は、

「try の中には“本当にエラーが起きうる行”だけを入れる」

ということです。

例えば、こう分けるとスッキリします。

text = input("番号を選んでください: ")

try:
    choice = int(text)
except ValueError:
    print("数字で入力してください。")
else:
    if choice == 1:
        print("足し算をします。")
    elif choice == 2:
        print("引き算をします。")
    elif choice == 0:
        print("終了します。")
    else:
        print("その番号のメニューはありません。")
Python

try の中は「intに変換する1行だけ」。
その後のロジックは else の中に分離されています。


3日目のミニアプリ:メモをファイルに保存する簡易アプリ+エラーハンドリング

仕様を決める

シンプルな「テキストメモアプリ」を作ります。

メニューで「1: メモを書く」「2: メモを読む」「0: 終了」
メモは memo.txt に保存する
ファイルがなくても落ちない
メニュー入力のミスでも落ちない

ここに、今日の try / except を組み込みます。


安全なファイル読み書き関数を書く

import os

FILENAME = "memo.txt"

def load_memo():
    if not os.path.exists(FILENAME):
        print("メモファイルがまだありません。空のメモとして扱います。")
        return ""

    try:
        with open(FILENAME, "r", encoding="utf-8") as f:
            text = f.read()
    except OSError:
        print("メモファイルを読み込めませんでした。")
        return ""

    return text

def save_memo(text):
    try:
        with open(FILENAME, "w", encoding="utf-8") as f:
            f.write(text)
    except OSError:
        print("メモファイルに書き込めませんでした。")
    else:
        print("メモを保存しました。")
Python

ここでの重要ポイントは、

FileNotFoundErroros.path.exists で事前に避けている
それでも起きうるファイル関連のエラーを OSError でまとめて捕まえている

という二段構えです。


メニュー+入力エラーハンドリングを組み合わせる

def input_menu_number(prompt):
    while True:
        text = input(prompt)

        try:
            value = int(text)
        except ValueError:
            print("数字で入力してください。\n")
            continue

        return value

def main():
    while True:
        print("\n=== メモアプリ ===")
        print("1: メモを書く")
        print("2: メモを読む")
        print("0: 終了")

        choice = input_menu_number("番号を選んでください: ")

        if choice == 1:
            print("\n=== メモを書く ===")
            print("今のメモ内容は次の通りです。")
            current = load_memo()
            print("----- 現在のメモ -----")
            print(current)
            print("----------------------")
            print("新しいメモ内容を入力してください。(上書きされます)")
            new_text = input("> ")
            save_memo(new_text)

        elif choice == 2:
            print("\n=== メモを読む ===")
            text = load_memo()
            print("----- メモ内容 -----")
            print(text)
            print("--------------------")

        elif choice == 0:
            print("終了します。")
            break

        else:
            print("その番号のメニューはありません。\n")

main()
Python

この時点で、

ファイルがなくても落ちない
ファイルが読めなくても落ちない
メニューに変な文字を入れても落ちない

という、「落ちないアプリ」になっています。


3日目で絶対に押さえてほしい本質

try / except は「境界を守るガードマン」

今日いちばん伝えたいのは、

try / except
ユーザー入力だけのためのものではなく、
「アプリと外の世界の境界」を守るための仕組み

だということです。

入力 → 型変換
ファイル → 読み書き
JSON → パース

こういった「外から来たもの」を扱う場所に
try / except を置いてあげると、
アプリは一気に“壊れにくく”なります。

そしてもう一つ大事なのは、

どこまでを try に入れるかを意識する
本当にエラーが起きうる行だけを囲う
それ以外のロジックは、通常の if / else に任せる

という「線引きのセンス」です。

4日目以降は、このエラーハンドリングを
JSON保存アプリや、より複雑な入力フローに組み込んでいきます。
今日のミニアプリを、自分なりに機能追加しながら
「どこに try を置くと気持ちいいか」を探ってみてください。

タイトルとURLをコピーしました