ファイルはちゃんとそこにあるのに、FileNotFoundError: [Errno 2] No such file or directory と言われてしまった。Pythonでファイルを読み込もうとして、そんな経験をしたことはありませんか?
エクスプローラーやFinderで見れば、たしかにファイルは存在しています。それなのにPythonだけが見つけてくれないので、とても不思議に感じますよね。
実はこのエラーの多くは、ファイルそのものではなく、Pythonがどこを探しているかに原因があります。この記事では、FileNotFoundErrorの意味とメッセージの読み方から、よくある原因と確実な直し方までを順番に見ていきます。
サンプルの実行結果は、手元のLinux環境のPython 3.11で確認したものです。WindowsやmacOSでも、エラーの名前と考え方は同じです。
FileNotFoundErrorとはどんなエラーなのか¶
まずは、このエラーが何を知らせているのかを押さえておきましょう。ひとことで言うと、指定した場所にファイルやフォルダが存在しないときに出るエラーです。
FileNotFoundErrorは、OSErrorという例外の仲間です。OSErrorは、ファイルやフォルダの操作のように、OSの機能を使った処理が失敗したときに使われる例外のグループです。
昔のPythonでは、ファイルが見つからない場合もOSErrorやIOErrorとして一括りにされていました。どの失敗かを見分けるには、errnoというエラー番号を自分で調べる必要があったのです。
この不便さを解消したのが、PEP 3151という提案です。Python 3.3から、FileNotFoundErrorやPermissionErrorのように、失敗の種類ごとに名前のついた例外が使えるようになりました。
実際にクラスの関係を確かめてみると、FileNotFoundErrorがOSErrorを継承していることがわかります。
print(FileNotFoundError.__mro__)
# (<class 'FileNotFoundError'>, <class 'OSError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)
そのため、except OSError と書けばFileNotFoundErrorも受け止められます。ただし、ほかの失敗までまとめて拾ってしまうので、ふだんはFileNotFoundErrorと具体的に書くのがおすすめです。
エラーメッセージの読み方¶
次に、メッセージのどこを見ればよいのかを確認しましょう。存在しないファイルを開くと、最後の行に次のように表示されます。
FileNotFoundError: [Errno 2] No such file or directory: 'data.csv'
角カッコの中のErrno 2は、OSが返したエラー番号です。2番はENOENTと呼ばれ、そういう名前のファイルやフォルダはありません、という意味を持っています。
そのあとの No such file or directory は、番号を人が読める言葉にしたものです。そして、いちばん大事なのがコロンのあとに表示されたパスです。
ここには、Pythonが実際に探しに行ったパスがそのまま表示されています。自分が思っていたパスと一文字ずつ見比べるだけで、原因がわかることも少なくありません。
エラーが起きた行を探すときは、メッセージより上に表示されるtracebackを読みます。どの行のopen()で失敗したのかを確認したい方は、こちらの記事が参考になります。 【関連記事】Pythonのエラー文はどこを読めばいい?初心者向けtracebackの見方
いちばん多い原因は実行する場所のずれ¶
ここからが本題です。ファイルがあるのに見つからないとき、まず疑ってほしいのがカレントディレクトリです。
カレントディレクトリとは、プログラムを実行したときに、いま自分がいるとみなされているフォルダのことです。作業フォルダと呼ばれることもあります。
相対パスはカレントディレクトリが基準になる¶
open("data.csv") のように、フォルダ名を書かずにファイル名だけを書く書き方を相対パスと呼びます。相対パスは、スクリプトが置いてある場所ではなく、カレントディレクトリを基準に探されます。
ここが、多くの人がつまずくポイントです。スクリプトとdata.csvが同じフォルダに並んでいても、別のフォルダから実行すると見つかりません。
たとえば、projectフォルダの中にmain.pyとdata.csvがあるとします。projectフォルダの外から python project/main.py と実行すると、Pythonは外側のフォルダでdata.csvを探してしまいます。
エディタの実行ボタンを使う場合も、どのフォルダで実行されるかは設定やワークスペースの開き方によって変わります。昨日は動いたのに今日は動かない、という不思議な現象の正体は、たいていこれです。
いまのカレントディレクトリは、os.getcwd()で確かめられます。エラーが出たら、まずこの1行を足して実行してみてください。
import os
print(os.getcwd())
私は10年ほどエンジニアとして開発に関わってきましたが、手元では動くのにcronで定期実行すると落ちる、という相談を何度も受けてきました。cronはホームディレクトリなど別の場所を作業フォルダにして動くことが多く、原因はほぼ毎回この相対パスでした。
スクリプトの場所を基準にすると確実¶
では、どこから実行しても同じファイルを読めるようにするには、どうすればよいのでしょうか。答えは、スクリプト自身の場所を基準にしてパスを組み立てることです。
Pythonでは、実行中のスクリプトのパスが file という特別な変数に入っています。これをpathlibのPathと組み合わせると、スクリプトのあるフォルダを確実に求められます。
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
DATA_FILE = BASE_DIR / "data.csv"
print(DATA_FILE)
resolve()は、パスを省略のない絶対パスに直すメソッドです。parentでその親フォルダを取り出し、スラッシュ演算子でファイル名をつなげています。
こうしておけば、カレントディレクトリがどこであっても、main.pyの隣にあるdata.csvを読めます。pathlibの使い方をもっと知りたい方は、こちらの記事をどうぞ。 【関連記事】Pythonのpathlibとは?ファイルパス操作を初心者向けに解説
なお、Jupyter Notebookでは file が使えないことがあります。その場合は、ノートブックのあるフォルダが作業フォルダになっていることが多いので、os.getcwd()で確認してからパスを決めましょう。
FileNotFoundErrorのよくある原因一覧¶
カレントディレクトリ以外にも、定番の原因がいくつかあります。エラーが出たら、次の表を上から順に確認してみてください。
| 原因 | よくある例 | 確認のしかた |
|---|---|---|
| 実行する場所がずれている | 別のフォルダから python main.py を実行した | os.getcwd()を表示する |
| ファイル名のタイプミス | date.csv と data.csv、全角と半角の混在 | メッセージのパスと実物を見比べる |
| 拡張子が隠れている | 実物の名前が data.csv.txt になっている | 拡張子を表示する設定にする |
| 大文字と小文字の違い | Data.csv を data.csv と書いた | Linuxでは別の名前として扱われる |
| 書き込み先のフォルダがない | open("output/result.txt", "w") でoutputがない | 親フォルダの有無を確かめる |
| Windowsのパスの書き方 | "C:\new\data.csv" の \n が改行になる | raw文字列か / を使う |
ひとつずつ、少しだけ補足しておきます。
拡張子の問題は、Windowsで特によく起こります。初期設定では拡張子が表示されないため、メモ帳で保存したCSVが data.csv.txt になっていても気づきにくいのです。
大文字と小文字の違いも、見落としやすい原因です。WindowsやmacOSの多くの環境では区別されませんが、Linuxでは Data.csv と data.csv は別のファイルになります。
実際に私も、Macで作ったアプリをLinuxのサーバーへ置いたとたん、画像の読み込みで落ちたことがあります。ファイル名の頭文字だけが大文字だったという、拍子抜けするほど小さな原因でした。
Windowsのパスに含まれる \n や \t が、改行やタブに化けてしまう問題もあります。これについては、こちらの記事でくわしく解説しています。 【関連記事】Pythonのエスケープシーケンスとraw文字列とは?WindowsのパスC:\newが改行に化ける落とし穴
書き込みでもFileNotFoundErrorは出る¶
FileNotFoundErrorは、読み込みのときだけのエラーだと思われがちです。ところが、書き込みモードで開いたときにも出ることがあります。
open()の "w" モードは、ファイルがなければ新しく作ってくれます。ただし作ってくれるのはファイルだけで、途中のフォルダまでは作ってくれません。
with open("output/result.txt", "w", encoding="utf-8") as f:
f.write("集計結果")
# FileNotFoundError: [Errno 2] No such file or directory: 'output/result.txt'
outputフォルダが存在しないと、このようにエラーになります。対処法は、書き込む前にフォルダを作っておくことです。
from pathlib import Path
out_path = Path("output/result.txt")
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text("集計結果", encoding="utf-8")
parents=Trueは途中のフォルダもまとめて作る指定で、exist_ok=Trueはすでにあってもエラーにしない指定です。この2つをセットで覚えておくと便利です。
open()やwith文の基本をおさらいしたい方は、こちらの記事も読んでみてください。 【関連記事】Pythonのファイル読み書きとは?open()とwith文の基本を初心者向けに解説
似たエラーとの違い¶
ファイル操作では、FileNotFoundError以外にもよく似たエラーに出会います。どれもOSErrorの仲間なので、名前で見分けられるようにしておきましょう。
代表的なものを、次の表にまとめました。
| エラー | 意味 | よくある場面 |
|---|---|---|
| FileNotFoundError | ファイルやフォルダが存在しない | パスの間違い、フォルダの作り忘れ |
| PermissionError | 存在するが権限がなくて開けない | 読み取り専用の場所に書き込もうとした |
| IsADirectoryError | ファイルのつもりがフォルダだった | open()にフォルダのパスを渡した |
| FileExistsError | 作ろうとしたものがすでにある | os.mkdir()で同じ名前のフォルダを作った |
手元のLinux環境でフォルダをopen()すると、IsADirectoryError: [Errno 21] Is a directory と表示されました。同じ操作でもOSによって出る例外が変わることがあるので、メッセージの名前をよく読むことが大切です。
PermissionErrorが出た場合は、ファイルは見つかっています。パスを見直すのではなく、権限や、ほかのアプリがファイルを開いたままになっていないかを確かめましょう。
FileNotFoundErrorに強いコードを書くには¶
ここまでで、原因の見つけ方はひととおりそろいました。最後に、ファイルが見つからないときでも慌てずに済むコードの書き方を紹介します。
方法は大きく2つあります。先に存在を確かめる方法と、まず開いてみて失敗したら対処する方法です。
先に存在を確かめる方法¶
Path.exists()を使うと、ファイルがあるかどうかを事前に確かめられます。読みやすく、初心者にもわかりやすい書き方です。
ただし、確かめた直後にほかのプログラムがファイルを消すと、結局open()で失敗します。そのため、Pythonではもうひとつの書き方が好まれることも多いです。
まず開いてみる方法¶
もうひとつは、try-exceptでFileNotFoundErrorを受け止める書き方です。次の例では、見つからなかったパスと作業フォルダを表示して、調査の手がかりを残しています。
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
DATA_FILE = BASE_DIR / "data.csv"
def load_lines(path):
try:
with open(path, encoding="utf-8") as f:
return f.read().splitlines()
except FileNotFoundError as e:
print(f"ファイルが見つかりません: {e.filename}")
print(f"いまの作業フォルダ: {Path.cwd()}")
return []
lines = load_lines(DATA_FILE)
print(f"{len(lines)}行を読み込みました")
例外オブジェクトのfilename属性には、見つからなかったパスが入っています。エラーをただ隠すのではなく、どこを探して失敗したのかを必ず表示するのがポイントです。
ファイルがない状態で実行すると、探しに行った絶対パスと作業フォルダが表示され、0行を読み込みましたと続きます。data.csvを置いてから実行すれば、中身の行数が表示されます。
注意したいのは、except FileNotFoundError: pass のように何もせずに握りつぶすことです。必須の設定ファイルが読めていないのに処理が進むと、もっと分かりにくい別のエラーに化けてしまいます。
なくても困らないファイルなら空で続け、なくては困るファイルならメッセージを出して止める。この線引きを意識するだけで、コードの安心感がずいぶん変わります。
try-exceptの基本を確認したい方は、こちらの記事でくわしく解説しています。 【関連記事】Pythonのtry・except・else・finallyとは?例外処理の基本の流れ
まとめ¶
FileNotFoundErrorは、指定したパスにファイルやフォルダが存在しないことを知らせるエラーです。メッセージのコロンのあとに表示されるパスは、Pythonが実際に探した場所なので、まずはそこを読みましょう。
ファイルがあるのに見つからないときは、相対パスがカレントディレクトリ基準で探されることを思い出してください。os.getcwd()で作業フォルダを確かめ、必要なら file を基準にパスを組み立てると確実です。
ほかにも、拡張子の隠れや大文字小文字の違い、書き込み先フォルダの作り忘れなど、原因の定番はある程度決まっています。今回の一覧表を手元に置いておけば、次に同じエラーが出ても落ち着いて対処できるはずです。
ここまでお読みいただきありがとうございました。