Pythonでファイルの読み込み、書き込み(作成・追記)
Pythonでのファイルの読み込み(入力)・書き込み(出力)ついて説明する。ファイルの中身を文字列やリストとして取得したり、ファイルを新規作成・上書き・追記したりできる。
ここではパス文字列を使う組み込み関数open()について説明するが、パスをオブジェクトとして操作するpathlibモジュールでも同様の処理が可能。ファイル全体を読み書きする場合はpathlibのread_text()などのメソッドを使うと楽。
また、Web上のファイルをダウンロード・保存したい場合は以下の記事を参照。
open(), withによるファイル読み書き(入出力)
基本的な書き方
読み込み・書き込みいずれの場合も組み込み関数open()でファイルを開く。
第一引数に指定したパス文字列が示すファイルオブジェクトが開かれる。読み込みか書き込みか、テキストかバイナリかは引数modeで指定する(後述)。
パスは、絶対パスでもカレントディレクトリからの相対パスでもよい。カレントディレクトリはos.getcwd()で確認、os.chdir()で変更できる。
テキストファイルはio.TextIOWrapper型として読み込まれる。
path = 'data/src/test.txt'
f = open(path)
print(type(f))
# <class '_io.TextIOWrapper'>
f.close()
上の例のようにopen()したファイルオブジェクトはclose()メソッドでクローズする必要がある。withブロックを使うとブロックの終了時に自動的にクローズされ、閉じ忘れがなくなるので便利。
with open(path) as f:
    print(type(f))
# <class '_io.TextIOWrapper'>
with open() as xxx:のxxxには任意の名前を使用できる。open()でオープンしたファイルオブジェクトにxxxという名前をつけてブロック内で使用するイメージ。fが使われることが多いが、他の名前でも問題ない。
エンコーディング指定: 引数encoding
テキストファイル読み込み時のデコード、書き込み時のエンコードで使われるエンコーディングはopen()の引数encodingで指定する。
encodingに指定する文字列は、大文字でも小文字でも、ハイフン-でもアンダースコア_でもよい。例えば'UTF-8'でも'utf_8'でもOK。
Pythonがサポートしているエンコーディングは公式ドキュメントを参照。日本語環境で使われるものとしてはcp932, euc_jp, shift_jis, utf_8などがある。
encodingのデフォルト値はプラットフォーム依存。locale.getpreferredencoding()で確認できる。
import locale
print(locale.getpreferredencoding())
# UTF-8
テキストファイルの読み込み
読み込み用でファイルオープン: mode='r'
open()の引数modeを'r'とすると読み込みモードでファイルが開かれる。引数modeのデフォルト値は'r'なので省略してもよい。以降の例では省略している。
mode='r'で第一引数に存在しないパスを指定するとエラー(FileNotFoundError)。
# with open('data/src/test_error.txt') as f:
#     print(type(f))
# FileNotFoundError: [Errno 2] No such file or directory: 'data/src/test_error.txt'
ファイル全体を文字列として読み込み: read()
ファイルオブジェクトのread()メソッドで、ファイル全体を文字列として取得できる。
with open(path) as f:
    s = f.read()
    print(type(s))
    print(s)
# <class 'str'>
# line 1
# line 2
# line 3
ファイルオブジェクトはwithブロックの終了時にクローズされるが、当然ながら、代入した変数はwithブロックの外(ファイルオブジェクトをクローズした後)でも使える。
with open(path) as f:
    s = f.read()
print(s)
# line 1
# line 2
# line 3
ファイル全体をリストとして読み込み: readlines()
ファイルオブジェクトのreadlines()メソッドで、ファイル全体を行ごとに分割したリストとして取得できる。最終行以外は末尾に改行コード\nを含む。
with open(path) as f:
    l = f.readlines()
    print(type(l))
    print(l)
# <class 'list'>
# ['line 1\n', 'line 2\n', 'line 3']
末尾の改行コードを削除するにはリスト内包表記を使って各要素でrstrip()を呼ぶ。
- 関連記事: Pythonで文字列の一部を削除(stripなど)
 - 関連記事: Pythonリスト内包表記の使い方
 
with open(path) as f:
    l_strip = [s.rstrip() for s in f.readlines()]
    print(l_strip)
# ['line 1', 'line 2', 'line 3']
ファイルを一行ずつ読み込み: readline()
ファイルオブジェクトをそのままfor文で回すと、一行ずつ文字列(末尾に改行コード含む)として取得できる。ここではrepr()で改行コードをそのまま表示している。
with open(path) as f:
    for s_line in f:
        print(repr(s_line))
# 'line 1\n'
# 'line 2\n'
# 'line 3'
一行ずつ進めていくにはnext()を使う。行数を超えるとエラー。
with open(path) as f:
    print(repr(next(f)))
    print(repr(next(f)))
    print(repr(next(f)))
#     print(repr(next(f)))
#     StopIteration:
# 'line 1\n'
# 'line 2\n'
# 'line 3'
ファイルオブジェクトのreadline()メソッドでも一行ずつ取得できるが、EOF(終端)以降もエラーにならず、空文字列''が返され続ける。
with open(path) as f:
    print(repr(f.readline()))
    print(repr(f.readline()))
    print(repr(f.readline()))
    print(repr(f.readline()))
    print(repr(f.readline()))
# 'line 1\n'
# 'line 2\n'
# 'line 3'
# ''
# ''
テキストファイルの書き込み(新規作成・上書き)
書き込み用でファイルオープン: mode='w'
open()の引数modeを'w'とすると書き込みモードでファイルが開かれる。
ファイルが存在しなければ新規作成、存在していれば上書きして保存される(既存の内容は削除)。追記・挿入については後述。
新規作成の場合、ファイルの直上までのディレクトリ(フォルダ)が存在していないとエラー(FileNotFoundError)となるので注意。
# with open('data/src/new_dir/test_w.txt', mode='w') as f:
#     f.write(s)
# FileNotFoundError: [Errno 2] No such file or directory: 'data/src/new_dir/test_w.txt'
新しいディレクトリにファイルを新規作成したい場合は以下の記事を参照。
文字列を書き込み: write()
ファイルオブジェクトのwrite()メソッドで、文字列を書き込むことができる。
path_w = 'data/temp/test_w.txt'
s = 'New file'
with open(path_w, mode='w') as f:
    f.write(s)
with open(path_w) as f:
    print(f.read())
# New file
改行を含む文字列もそのまま書き込まれる。
s = 'New line 1\nNew line 2\nNew line 3'
with open(path_w, mode='w') as f:
    f.write(s)
with open(path_w) as f:
    print(f.read())
# New line 1
# New line 2
# New line 3
文字列の改行については以下の記事も参照。
write()の引数に指定できるのは文字列のみ。それ以外の型だとエラー(TypeError)となる。整数intや浮動小数点数floatなど他の型を書き込みたい場合はstr()で文字列に変換してwrite()に渡す。
リストを書き込み: writelines()
ファイルオブジェクトのwritelines()メソッドで、リストを書き込むことができる。改行コードは挿入されず、要素がそのまま連結されて書き込まれる。
l = ['One', 'Two', 'Three']
with open(path_w, mode='w') as f:
    f.writelines(l)
with open(path_w) as f:
    print(f.read())
# OneTwoThree
リストの要素ごとに改行して書き込むには、改行コードとjoin()メソッドで改行を含む文字列を作成し、write()メソッドで書き込む。
with open(path_w, mode='w') as f:
    f.write('\n'.join(l))
with open(path_w) as f:
    print(f.read())
# One
# Two
# Three
writelines()の引数に指定できるのは文字列を要素とするリストのみ。整数intや浮動小数点数floatなど他の型を要素とするリストを書き込みたい場合は文字列のリストに変換する必要がある。以下の記事を参照。
空のファイルを作成: pass文
空のファイルを作成したい場合は書き込みモード(mode='w')のopen()でファイルを新規作成した上で中身を何も書き込まなければよい。
文法上、withブロックに何か記述する必要があるので、何もしないpass文を使う。
- 関連記事: Pythonのpass文の意味と使い方
 
with open('temp/empty.txt', 'w'):
    pass
ファイルが存在しない場合のみ書き込み(新規作成のみ)
mode='w'で誤って既存のファイルを指定すると上書きされてしまう。
ファイルが存在しない場合のみ書き込み、つまり、新規作成のみで上書きは禁止するには以下の二つの方法がある。
新規作成専用でファイルオープン: mode='x'
open()の引数modeを'x'とすると、ファイルが存在する場合はエラー(FileExistsError)となり、ファイルが存在しない場合のみファイルが書き込み用として開かれ保存(新規作成)される。
# with open(path_w, mode='x') as f:
#     f.write(s)
# FileExistsError: [Errno 17] File exists: 'data/src/test_w.txt'
try, exceptで例外処理をすれば、ファイルが存在しない場合は新規作成、ファイルが存在している場合は何もしないという処理が可能。
try:
    with open(path_w, mode='x') as f:
        f.write(s)
except FileExistsError:
    pass
ファイルの存在を確認してからオープン
標準ライブラリのosモジュールのos.path.isfile()でファイルの存在確認ができる。これを利用してファイルが存在しない場合のみopen()で書き込み処理を行うことも可能。
import os
if not os.path.isfile(path_w):
    with open(path_w, mode='w') as f:
        f.write(s)
テキストファイルの追記・挿入
末尾に追記: mode='a'
open()の引数modeを'a'とすると追記モードでファイルが開かれる。
write(), writelines()を使うと既存のファイルの末尾に追記される。
with open(path_w, mode='a') as f:
    f.write('Four')
with open(path_w) as f:
    print(f.read())
# One
# Two
# ThreeFour
最終行を追加したい場合は改行コードも含めて追記する。
with open(path_w, mode='a') as f:
    f.write('\nFour')
with open(path_w) as f:
    print(f.read())
# One
# Two
# ThreeFour
# Four
なお、存在しないファイルに対してはmode='w'と同じくファイルが新規作成される。
先頭、途中に挿入
mode='r+'
open()の引数modeを'r+'とすると、読み書きモードでファイルが開かれる。
write(), writelines()は、既存のファイルの先頭から上書きする。
with open(path_w, mode='r+') as f:
    f.write('12345')
with open(path_w) as f:
    print(f.read())
# 12345wo
# ThreeFour
# Four
上の例ではOne\nTwoが先頭から12345で上書きされて12345woとなる。\nは1文字として扱われる。
ファイルオブジェクトのメソッドseek()で位置を移動して書き込むこともできるが、行単位ではなく文字単位で位置を指定する必要がある。この場合も上書き。
with open(path_w, mode='r+') as f:
    f.seek(3)
    f.write('---')
with open(path_w) as f:
    print(f.read())
# 123---o
# ThreeFour
# Four
readlines(), insert()を使う
巨大なテキストファイルでなければ、readlines()で全体をリストとして読み込んで処理するのが楽。リストのメソッドinsert()で、先頭や途中の行に新たな文字列を挿入できる。
readlines()でファイルをリストとして読み込んで、insert()で要素を挿入した後にwritelines()で書き込む。
insert()の第一引数で挿入する行数を指定できる。
with open(path_w) as f:
    l = f.readlines()
l.insert(0, 'FIRST\n')
with open(path_w, mode='w') as f:
    f.writelines(l)
with open(path_w) as f:
    print(f.read())
# FIRST
# 123---o
# ThreeFour
# Four
リストへの要素の追加・挿入については以下の記事を参照。
リストの要素を削除すると行の削除になる。
バイナリファイルの読み書き
これまで説明したmodeの末尾にbをつけるとバイナリファイルとしての読み書きとなる。
例えばmode='rb'はバイナリファイルの読み込み、mode='ab'はバイナリファイルの末尾に追記となる。
テキストファイルと同様、バイナリファイルでもファイルオブジェクトのメソッドとしてread(), readline(), readlines(), write(), writelines()が使える。
バイナリファイルの編集のほか、Webサイトからダウンロードした画像を保存する際などにも使う。
画像ファイルを開いて処理して保存する場合は、PillowやOpenCVなどのライブラリを使う。