ファイル入出力と例外処理——CSV・JSON を読み書きする
レッスン6:ファイル入出力と例外処理——CSV・JSON を読み書きする
このレッスンで学ぶこと
- ファイルの open と with 文の基本を理解する
- テキストファイルを行ごとに読み書きする
- CSV ファイルを
csvモジュールで読み書きする - JSON ファイルを
jsonモジュールで読み書きする try/exceptによる例外処理を扱える- よくある例外(
FileNotFoundError・ValueError・KeyError)の意味を理解する - 文字エンコーディング(UTF-8 と Shift_JIS)の基本を押さえる
前回のレッスンでは、関数とモジュールを扱いました。コードに名前を付け、組み合わせる力が身につきました。本レッスンでは、Python の業務利用で外せない 2 つの土台を扱います。「ファイルの読み書き」と「エラーの処理」です。CSV や JSON を扱えるようになり、エラーがあっても止まらないスクリプトを書けるようになると、業務自動化の世界が大きく開けます。
ファイルを開く——open と with
Python でファイルを扱うときの基本は open() 関数です。
open の基本
file = open("memo.txt", "r", encoding="utf-8")
content = file.read()
file.close()
print(content)
open の引数は次の通りです。
- 第 1 引数:ファイルパス(文字列)
- 第 2 引数:モード(
"r"読み込み、"w"書き込み、"a"追記、"r+"読み書き) encoding:文字コード(後述)
最後に close() を忘れるとファイルが開いたままになり、トラブルの元になります。
with 文——閉じ忘れを防ぐ
実務では、with 文を使ってファイルを開くのが定番です。with ブロックを抜けると、自動的にファイルが閉じられます。
with open("memo.txt", "r", encoding="utf-8") as file:
content = file.read()
print(content)
as file で、ファイルオブジェクトに file という名前を付けています。ブロックを抜けたら自動で閉じられるので、close() を書く必要がありません。本コースでは、ファイル操作に with 文を使う形に統一します。
モードと用途
| モード | 用途 |
|---|---|
"r" |
読み込み(ファイルがないとエラー) |
"w" |
書き込み(既存の内容を消して、新しく書く) |
"a" |
追記(既存の内容の末尾に追加) |
"r+" |
読み書き両用 |
「"w" モードは中身を消す」ことに注意します。大事なファイルを "w" で開く前は、バックアップを取るのが安全です。
💡 ポイント ファイルを扱うときは
with文を使い、open()の戻り値をasで名前付けします。モードは"r"(読み)、"w"(書き、中身は消える)、"a"(追記)。"w"で大事なファイルを開かないよう注意します。
テキストファイルの読み書き
全文を一度に読む
with open("memo.txt", "r", encoding="utf-8") as file:
content = file.read()
print(content)
file.read() で、ファイル全文を 1 つの文字列として読みます。
1 行ずつ読む
大きなファイルでは、全文を一度に読むとメモリを使いすぎることがあります。for 文で 1 行ずつ読むのが、業務での定番です。
with open("memo.txt", "r", encoding="utf-8") as file:
for line in file:
print(line.rstrip()) # 改行文字を取って表示
line.rstrip() で、末尾の改行(\n)を取り除きます。
書き込み
with open("output.txt", "w", encoding="utf-8") as file:
file.write("こんにちは\n")
file.write("Python\n")
file.write() で文字列を書き込みます。改行は明示的に \n を入れます。
追記
with open("log.txt", "a", encoding="utf-8") as file:
file.write("2026-06-20 14:30 ログ追加\n")
"a" モードで開くと、既存の内容を消さずに末尾に追加します。ログファイルの記録に使う典型例です。
💡 ポイント テキストファイルの読み書きは
read()(全文)、for line in file(1 行ずつ)、write()(書き込み)、"a"モードでの追記が基本です。
文字エンコーディング——UTF-8 と Shift_JIS
業務でファイルを扱うときに必ず出てくるのが「文字エンコーディング」の問題です。
文字エンコーディングとは
コンピュータは文字をそのまま扱えず、内部では数値に変換して保存します。この「文字 ↔ 数値」のルールを「文字エンコーディング」と呼びます。
主なエンコーディング
| エンコーディング | 主な用途 |
|---|---|
| UTF-8(推奨) | 国際標準、Web の標準、Python 標準、Mac/Linux の標準 |
| Shift_JIS(SJIS、CP932) | 日本の Windows 環境、古い Excel ファイル |
2026 年現在、新しいファイルはほぼ UTF-8 で作るのが標準です。ただし、社内の古い Excel から書き出した CSV は Shift_JIS のままになっていることもあり、業務では両方を扱う場面が残ります。
エンコーディングを指定して読む
# UTF-8 で読む
with open("data.csv", "r", encoding="utf-8") as file:
content = file.read()
# Shift_JIS で読む(古い Windows の Excel CSV はこちら)
with open("old_data.csv", "r", encoding="cp932") as file:
content = file.read()
encoding を間違えると、UnicodeDecodeError が発生したり、文字化けします。「文字化けしたら、まずエンコーディングを疑う」というのが、業務での定石です。
💡 ポイント 文字エンコーディングは UTF-8(推奨)と Shift_JIS(古い日本の Windows)の 2 つを覚えます。
encodingを間違えるとエラーや文字化けが起こるので、業務では「文字化けしたら encoding を疑う」を最初の確認項目に。
CSV ファイルの読み書き
業務でもっとも多く扱うファイル形式が CSV(Comma-Separated Values、カンマ区切り)です。Excel から書き出した CSV を Python で読む、Python で集計した結果を CSV に書き出して Excel で開く、というのが定番の流れです。
csv モジュールで読む
import csv
with open("sales.csv", "r", encoding="utf-8") as file:
reader = csv.reader(file)
for row in reader:
print(row)
csv.reader は、1 行を「文字列のリスト」として返します。1 行目がヘッダーなら、それも 1 つのリストとして読まれます。
辞書として読む——DictReader
csv.DictReader を使うと、1 行を「辞書」として読めます。1 列目の名前がキーになるので、業務スクリプトでは可読性が大幅に上がります。
import csv
with open("sales.csv", "r", encoding="utf-8") as file:
reader = csv.DictReader(file)
for row in reader:
print(f"{row['name']}:{row['price']} 円")
ヘッダー行が name,price で、データが 山田,1000 などになっている CSV を読むと、row['name'] で "山田"、row['price'] で "1000"(文字列)が取れます。数値として使うときは int(row['price']) で型変換します。
CSV を書く
import csv
data = [
["name", "price"],
["山田", 1000],
["鈴木", 2500],
]
with open("output.csv", "w", encoding="utf-8", newline="") as file:
writer = csv.writer(file)
writer.writerows(data)
newline="" は Windows で余分な空行が入るのを防ぐためのおまじないです。writerow(1 行ずつ)と writerows(複数行まとめて)を使い分けます。
辞書のリストを書く——DictWriter
import csv
data = [
{"name": "山田", "price": 1000},
{"name": "鈴木", "price": 2500},
]
with open("output.csv", "w", encoding="utf-8", newline="") as file:
writer = csv.DictWriter(file, fieldnames=["name", "price"])
writer.writeheader()
writer.writerows(data)
DictWriter は辞書のリストをそのまま CSV に書き出せます。fieldnames で列の順序を指定します。
💡 ポイント CSV の読み書きは
csvモジュールが標準。DictReader/DictWriterを使うと、辞書として扱えて業務での可読性が上がります。Windows での書き込みではnewline=""を忘れずに。
JSON ファイルの読み書き
JSON(JavaScript Object Notation、ジェイソン)は、Web API でもっとも使われるデータ形式です。社内システム間のデータ連携、Web からの取得データ、設定ファイルなど、業務でも頻繁に登場します。
JSON の構造
JSON は、Python の辞書とリストにそのまま対応する形式です。
{
"name": "山田",
"age": 30,
"hobbies": ["読書", "登山"]
}
これを Python の辞書として読むと:
{
"name": "山田",
"age": 30,
"hobbies": ["読書", "登山"],
}
ほぼ同じ見た目になります。
JSON を読む
import json
with open("data.json", "r", encoding="utf-8") as file:
data = json.load(file)
print(data["name"]) # 山田
print(data["hobbies"]) # ['読書', '登山']
json.load(file) で、JSON ファイルを Python の辞書/リストに変換します。
JSON を書く
import json
data = {
"name": "山田",
"age": 30,
"hobbies": ["読書", "登山"],
}
with open("output.json", "w", encoding="utf-8") as file:
json.dump(data, file, ensure_ascii=False, indent=2)
ensure_ascii=False を入れると、日本語が \uXXXX のエスケープ表記にならず、そのまま日本語で書かれます。indent=2 でインデントを 2 スペース付けて見やすくします。
文字列との変換——dumps と loads
ファイルではなく、文字列として扱いたいときは dumps/loads を使います。
import json
data = {"name": "山田", "age": 30}
# 辞書 → JSON 文字列
text = json.dumps(data, ensure_ascii=False)
print(text) # {"name": "山田", "age": 30}
# JSON 文字列 → 辞書
text = '{"name": "鈴木", "age": 25}'
data = json.loads(text)
print(data["name"]) # 鈴木
Web API からの応答を扱うときに、loads をよく使います(次のレッスン 7 で扱います)。
💡 ポイント JSON は Python の辞書/リストにそのまま対応する形式。
json.load/json.dumpでファイル操作、json.loads/json.dumpsで文字列操作。ensure_ascii=Falseとindentを覚えると業務で重宝します。
例外処理——try と except
Python のコードを書いていると、必ずエラー(例外)が起こります。ファイルが見つからない、文字列が数値に変換できない、辞書のキーが存在しない——いずれも例外です。これらを安全に処理する仕組みが「例外処理」です。
例外処理の発想
| 場面 | 例外を処理しない場合 | 例外を処理した場合 |
|---|---|---|
| ファイルがない | スクリプトが止まる | エラーメッセージを表示して次に進める |
| 数値変換に失敗 | スクリプトが止まる | デフォルト値を使う |
| ネットワーク失敗 | スクリプトが止まる | 数秒待って再試行 |
try/except の基本
try:
age_text = "abc"
age = int(age_text)
print(age)
except ValueError:
print("数値に変換できませんでした")
try ブロックの中で例外が起こると、except ブロックに飛びます。スクリプト全体が止まらず、処理を続けられます。
制御フロー図
flowchart TD
A[try ブロック] --> B{例外が起きた?}
B -->|No| C[try の続き]
B -->|Yes| D[該当する except へ]
C --> E[else があれば実行]
D --> F[finally があれば実行]
E --> F
F --> G[次の処理へ]
複数の例外を処理する
try:
value = int("abc")
except ValueError:
print("値の変換に失敗")
except FileNotFoundError:
print("ファイルが見つからない")
except Exception as e:
print(f"想定外のエラー: {e}")
Exception as e で、想定外のエラーを最後に受け止めるのが定番です。
else と finally
try:
with open("data.txt", "r", encoding="utf-8") as file:
content = file.read()
except FileNotFoundError:
print("ファイルが見つかりません")
else:
print("読み込み成功")
print(content)
finally:
print("処理を終了します")
else:try が例外なく完了したときに実行されるfinally:例外があってもなくても必ず実行される(後片付けに使う)
💡 ポイント 例外処理は
try/exceptで書き、業務スクリプトが途中で止まらないようにします。else(成功時のみ)、finally(必ず)も覚えると、複雑な処理を安全に書けます。
よくある例外の種類
業務で出会う例外は、典型例があります。意味を知っておくと、エラーメッセージから原因をつかみやすくなります。
| 例外 | 意味 | よくある原因 |
|---|---|---|
FileNotFoundError |
ファイルが見つからない | パスが間違っている、ファイル名のタイプミス |
PermissionError |
アクセス権限がない | 開かれている Excel ファイルを書き換えようとした |
ValueError |
値の変換に失敗 | int("abc") のような、形式が合わない変換 |
TypeError |
型が合わない | 文字列と数値を直接連結した("a" + 1) |
KeyError |
辞書のキーが存在しない | dict["age"] で age がない |
IndexError |
リストの範囲外 | list[100] でリストが 100 個未満 |
ZeroDivisionError |
0 で割った | 1 / 0 |
UnicodeDecodeError |
エンコーディングが合わない | UTF-8 で開くべき Shift_JIS のファイル、またはその逆 |
業務での例外処理の方針
すべての例外を try/except で包めばよい、というわけではありません。本コースの推奨は、次の使い分けです。
- 想定される例外だけ try/except で扱う:「CSV ファイルが見つからない可能性がある」「文字列に数字以外が混ざることがある」など、業務上ありえる例外
- 想定外の例外は止まらせる:開発中に気づかなかったエラーは、止まったほうが直しやすい
- 空の
except:は書かない:「すべての例外を黙って無視する」は、バグの温床
💡 ポイント よくある例外は
FileNotFoundError・ValueError・KeyError・UnicodeDecodeErrorなど。「想定される例外だけをtry/exceptで扱い、想定外は止まらせる」のが業務での推奨方針です。
講師の現場メモ:「Shift_JIS の社内 CSV と、UTF-8 の Web 連携で半日溶かした日」
私(平野)が、IT コンサル時代に、ある金融機関のレポート自動化案件を担当したときの話です。月次の営業報告書を作る作業で、3 つのデータソースを統合する必要がありました。
- 社内システム A から書き出した CSV(Excel で確認用に使われていた)
- 社内システム B からの CSV(Linux サーバー上で生成)
- 外部 Web サービスから取得した JSON データ
実装の前半は順調に進みました。各データを個別に読み、加工して、最終的な Excel に出力するスクリプトを 200 行ほどで書きました。動作確認も問題なし、と思った金曜の夕方、本番データで動かすと、A のデータだけ文字化けして読めません。
「あ、エンコーディングだな」と気づきました。社内システム A は古い社内 PC(Windows)で Excel から書き出されていて、Shift_JIS(cp932)で保存されていました。私のスクリプトは UTF-8 で読んでいたので、UnicodeDecodeError が出ます。
encoding="cp932" に変えたところ、エラーは消えました。「これで完了」と思ってデプロイした翌週、本番で別のエラーが起きました。今度は「ある顧客名に含まれる丸数字(①や②)が文字化けする」というもの。Shift_JIS では特殊文字の扱いに揺れがあり、cp932 でも丸数字や半角カナの一部が失われることがある、というのを思い出しました。
私は次のように対策しました。社内システム A のデータを読むときに、errors="replace" を指定して、デコードできない文字は ? に置き換える設定にし、その上で「丸数字が含まれていそうな顧客名は、後処理で別途確認」という仕組みを入れました。
with open("system_a.csv", "r", encoding="cp932", errors="replace") as file:
reader = csv.DictReader(file)
for row in reader:
if "?" in row["customer_name"]:
print(f"要確認:{row['customer_name']}")
continue
# ……正常処理
加えて、try/except で UnicodeDecodeError を明示的に捕まえ、エラー時には「どのファイルのどの行で失敗したか」をログに出力する仕組みも入れました。
try:
process_csv("system_a.csv", encoding="cp932")
except UnicodeDecodeError as e:
log.error(f"エンコーディングエラー:system_a.csv: {e}")
raise # 想定外の場合は止まらせる
このときに私が痛感したのは、業務での Python スクリプトは「動く」だけでは不十分で、「止まらない」「止まったときに原因がわかる」が大事だ、ということです。例外処理は「うざい余計な作業」に見えますが、業務で半年間動き続けるスクリプトを書くなら必須の発想です。
もう 1 つの教訓は、エンコーディングは日本のビジネス現場で必ず出会う問題、ということです。Mac/Linux 育ちの方は UTF-8 中心で意識しないかもしれませんが、日本の Windows 環境では Shift_JIS(cp932)が残り続けています。業務スクリプトで CSV を扱うときは、最初から「UTF-8 と Shift_JIS の両方を意識する」発想で書くと、半日溶かす事故を防げます。本コースでエンコーディングを扱うのは、皆さんに同じ事故を踏んでほしくないからです。
まとめ
このレッスンでは、以下のことを学びました。
- ファイル操作は
with open(...) as file:で書く。モードは"r"(読み)、"w"(書き、中身は消える)、"a"(追記) - テキストは
read()(全文)、for line in file(1 行ずつ)、write()(書き込み)が基本 - 文字エンコーディングは UTF-8(推奨)と Shift_JIS(cp932、古い日本の Windows)の 2 種類が代表。
encodingの指定を間違えると文字化けや UnicodeDecodeError に - CSV は
csvモジュール。DictReader/DictWriterで辞書として扱える。Windows での書き込みはnewline=""を忘れない - JSON は
jsonモジュール。load/dump(ファイル)、loads/dumps(文字列)。ensure_ascii=Falseで日本語を読みやすく - 例外処理は
try/except/else/finallyで構成。想定される例外だけ扱い、想定外は止まらせる - よくある例外:
FileNotFoundError、ValueError、KeyError、UnicodeDecodeErrorなど
次のレッスンでは、業務で使えるライブラリの入口(pandas、openpyxl、requests)に立ち、業務自動化のスクリプトを組み立てる発想を学びます。
確認クイズ
このレッスンの理解度をチェックしましょう。