プログラミングでは、何らかのデータを読み出したり、書き出したりすることが数多くあります。
データの保存先は、データベースが用いられることが一般的ですが、まとまった文章や構造化されていないデータなどはファイルで保存されることも多くあります。
そのため、ファイル操作はプログラミングを学習する中で重要項目の1つとなります。
本記事では、Pythonでよく使われるファイル操作について解説します。
プログラミング初心者の方の学習や、忘れてしまった方の復習として、参考にしていただければ幸いです。
記載しているプログラムは、Python3.6.8を使って動作確認をしています。
Pythonのファイル操作
Pythonのファイル操作として、以下の内容を採り上げます。
- ファイル操作を扱うモジュール
- ファイルの読み込み
- ファイルの書き込み
- ファイルの削除
- ファイルの存在確認
- ファイルのコピー
- ディレクトリの一覧取得
- ディレクトリの作成
- ディレクトリの削除
ファイル操作を扱うモジュール
Pythonのファイル読み書きは、組込み関数の open を使って行います。組込み関数のため、importは不要です。
ファイルパスに関連する操作は os.path 、高機能な操作には shutil などのモジュールを使います。
必要に応じて、モジュールをimportして使います。
ファイルの読み込み
ファイルを読み込むには、open を読み込みモードで指定します。
try: with open('./files/testfile.txt', 'r') as f: s = f.read() print(s) except Exception as e: print(e)
上記のプログラムでは、filesディレクトリのtestfile.txtファイルを読み込んで、内容をコンソールに出力しています。
ファイルが存在しないなどにより、読み込みに失敗した場合は、例外が発生し、except文で例外情報をコンソールに出力しています。
この方法では、ファイルの中身を一度にすべて読みこんでしまうため、大きなファイルだと時間がかかったり、メモリを大量に消費してしまいます。
ファイルを1行ずつ読み込むには、readlineを使います。readlineを呼出す毎に1行が取得されます。
try: with open('./files/testfile.txt', 'r') as f: s = f.readline() print(s) except Exception as e: print(e)
ファイルの書き込み
ファイルの書き込みは、新規にファイルを作成して書き込む方法と、既存のファイルに追記する方法があります。
新規ファイルへの書き込み
新規にファイルを作成して書き込むには、open を書き込みモードで指定します。
s = "新規にファイルを作成するテスト" try: with open('./files/writefile.txt', 'w') as f: f.write(s) except Exception as e: print(e)
上記のプログラムでは、filesディレクトリにwritefile.txtというファイルを作成し、その中に「新規にファイルを作成するテスト」という文字列を書き込んでいます。
このプログラムの実行後にファイルが作成されますが、毎回新規にファイルを作成するため、何度実行してもファイルの中身は同じ結果になります。
既存ファイルへの追記
既存のファイルへ追記するには、open を追記モードで指定します。
s = "既存にファイルに追記するテスト" try: with open('./files/writefile.txt', 'a') as f: f.write(s) except Exception as e: print(e)
上記のプログラムでは、filesディレクトリのwritefile.txtというファイルに、「既存ファイルに追記するテスト」という文字列を追記しています。
既存ファイルが存在しない場合は、新規にファイルを作成して書き込みます。
ファイルの削除
ファイルを削除するには、osモジュールのremoveを使います。
import os try: os.remove('./files/writefile.txt') except Exception as e: print(e)
上記のプログラムでは、filesディレクトリのwritefile.txtというファイルを削除します。
ファイルが存在しない場合は、例外が発生します。
ファイルの存在確認
ファイルが存在するかどうかを確認するには、os.pathモジュールの exists を使います。
print(os.path.exists('./files/testfile.txt')) # -> True print(os.path.exists('./files/notexistsfile.txt')) # -> False print(os.path.exists('./files')) # -> True
exsitsは、ファイルが存在する場合はTrueを返し、存在しない場合はFalseを返します。
ディレクトリに対しても存在確認をすることができます。
ファイルかディレクトリかを判別するには、os.pathモジュールの isfile または isdir で確認します。
print(os.path.isfile('./files/testfile.txt')) # -> True print(os.path.isdir('./files/testfile.txt')) # -> False print(os.path.isfile('./files')) # -> False print(os.path.isdir('./files')) # -> True
ファイルのコピー
ファイルをコピーするには、shutilモジュールの copy を使います。
import shutil try: shutil.copy('./files/testfile.txt', './files/copyfile.txt') except Exception as e: print(e)
上記のプログラムでは、filesディレクトリにcopyfile.txtを作成しますが、すでに同ファイルが存在している場合は、上書いてコピーします。
copyはファイルの内容はコピーされますが、作成日時や変更日時などのメタデータはコピーしません。これらもコピーするには copy2 を使います。
try: shutil.copy2('./files/testfile.txt', './files/copyfile.txt') except Exception as e: print(e)
ディレクトリの一覧取得
ディレクトリのファイル一覧を取得するには、osモジュールの listdir を使います。
listdirは、ファイル名やディレクトリ名を配列で返します。
try: print(os.listdir('./files')) # -> ['testfile.txt', 'copyfile.txt', 'testdir'] except Exception as e: print(e)
上記のプログラムでは、filesディレクトリのファイルとディレクトリが取得され、その配下のファイルやディレクトリは取得されません。
配下のすべてのファイルやディレクトリを取得するには、globモジュールの glob を使います。
import glob try: print(glob.glob('./files/**', recursive=True)) # -> ['./files/', './files/testfile.txt', './files/copyfile.txt', './files/testdir', './files/testdir/testfile'] except Exception as e: print(e)
globは取得対象となる名称を正規表現で指定可能です。
ディレクトリの作成
ディレクトリを新規に作成するには、osモジュールの mkdir を使います。
try: os.mkdir('./files/testdir1') except Exception as e: print(e)
上記のプログラムでは、filesディレクトリの配下にtestdir1ディレクトリを作成しています。
すでにtestdir1ディレクトリが存在する場合にはエラーとなります。
複数階層のディレクトリを一気に作成するには、makedirs を使います。
exists_okオプションにTrueを指定すると、すでにディレクトリが存在している場合でもエラーになりません。
try: os.makedirs('./files/testdir2/testdir2', exist_ok=True) except Exception as e: print(e)
プログラムの中では、一時的にファイルを扱うためにディレクトリを作成したい場合があります。
プログラムの特性にもよりますが、基本的には他のプログラムや処理で使うディレクトリとは異なる専用のディレクトリを作るのが一般的です。
そのような用途の場合には、ランダムな名称でディレクトリを作成してくれるtempfileモジュールの TemporaryDirectory を使います。
import tempfile try: with tempfile.TemporaryDirectory() as tempdir: print(tempdir) # -> /tmp/tmp96lp1_r9 print(os.path.exists(tempdir)) # -> True print(os.path.exists(tempdir)) # -> False except Exception as e: print(e)
TemporaryDirectoryは、デフォルトでは環境変数に指定されたテンポラリディレクトリを使いますが、引数で自分で指定することも可能です。
with文を抜けると、作成したディレクトリは自動的に削除されます。
ディレクトリの削除
ディレクトリを削除するには、osモジュールの rmdir を使います。
try: os.rmdir('./files/testdir1') except Exception as e: print(e)
削除対象のディレクトリに、ファイルやディレクトリが存在する場合はエラーとなります。
ディレクトリのみ存在する状態であれば、osモジュールの removedirs を使います。削除対象のディレクトリがファイルが存在するなどにより削除できない場合はエラーとなります。
try: os.removedirs('./files/testdir2/testdir2') except Exception as e: print(e)
ディレクトリとファイルを一括で削除したい場合は、shutilモジュールの rmtree を使います。
try: shutil.rmtree('./files/testdir3', ignore_errors=True) except Exception as e: print(e)
上記のプログラムでは、testdir3ディレクトリを配下のファイルやディレクトリとともに一括で削除します。
デフォルトでは存在しないディレクトリを指定するとエラーとなりますが、ignore_errorsにTrueを指定するとエラーを抑制します。
複数の処理で1つのファイル操作を行う場合は要注意
ファイルはデータを保存しておく場所として使いやすいのですが、複数のプロセスやスレッドなどから同じファイルを操作しようとすると思わぬことが発生することがあるので注意が必要です。
僕がこれまでに携わったプロジェクトでも以下のようなことがありました。
- 複数のプロセスから同一のログファイルに書き込んだらファイルが壊れた
- 参照しようとしたら別の処理が先に消していた
- あるサーバが作成したファイルを別のサーバから参照しようとしたら、存在するはずのファイルが見えない状態だった
これらは、設計ミスやOSのファイル管理方法を理解出来ていないことが原因で発生したものでした。
ファイルはOSと密接に関わっているため、ローカル環境では上手く動いても、別の環境では意図通りに動かないといったことがあります。
ファイルを扱う際は、設計時点で問題ないかをきちんと議論することをおすすめします。
今回はPythonのファイル操作について解説しました。
以上、参考になれば幸いです。
コメント