Pythonプログラムで使用していたデータを永続的に保存するにはファイルが最もよく使われます。
いったんファイルへ保存すれば、後でそれを読み込んでデータを再利用することができます。またファイルからデータを読み込んでPythonに処理させることもよく行われます。
プログラムを書くうえで、ファイル入出力はなくてはならないものです。ここではPythonのファイル入出力の方法を説明します。
ファイル入出力の概要
Pythonでファイルの入出力を行うには、まずファイルを開く必要があります。
ファイルを開くには組み込みのopen()関数を使います。
open()関数の完全な引数とデフォルト値は次の通りです。
open(file, mode='r', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
ただし、重要な引数は次の4つだけです。ほとんどの場合、これさえ理解していれば十分です。
open(file, mode='r', encoding=None, newline=None)
引数fileにはファイル名(絶対パスか相対パス)を文字列で指定します。
引数modeは、どのモードでファイルを開くかを指示する文字列を渡します。
一般には次の2つの文字を組み合わせて指定します。
1つは次の文字のいずれかです。
文字 | 意味 |
---|---|
r | 読み込み用に開く(デフォルト) |
w | 書き込み用に開く。ファイルが存在しない場合は作成される。既存ファイルを開くと、まずファイルを空にする |
x | 書き込み用に開く。ファイルが存在しない場合は作成される。ファイルが存在する場合はFileExistsErrorが発生する |
a | 書き込み用に開く。ファイルが存在しない場合は作成される。ファイルが存在する場合は末尾に追記する |
もう1つには次の文字のいずれかを使います。
文字 | 意味 |
---|---|
t | テキストモード(デフォルト) |
b | バイナリモード |
デフォルトの文字は省略することができますが、ここでの例ではすべて明示的に記載します。
open()関数の呼び出しが成功すると、ファイルオブジェクトが返されます。ファイルへの読み書きは、このファイルオブジェクトの関数を使って行います。
最後にファイルは閉じる必要があります。それにはファイルオブジェクトのclose()関数を使います。
書き込み関数でファイルへ書き込んでも、それがすぐにディスクへ書き込まれると考えてはいけません。close()関数を呼び出すと、書き込んだ内容がディスクへも書き込まれることが保証されます。
また、ファイルを開くとOSの限りあるリソースを使います。そのため必要なくなったらすぐにファイルを閉じるのがベストプラクティスです。
残りの引数encodingと引数newlineは後ほど説明します。
テキストファイルの入出力
テキストファイルは文字で構成されます。テキストには行があり、各行は改行コードで区切られます。そして文字列をテキストに保存するにはエンコードする必要があります。これらはバイナリファイルにはないテキストファイルの特徴です。
テキストファイルの入出力では、テキストモード(t)でファイルを開きます。
テキストファイルへの出力
テキストをファイルへ書き込む方法から見ていきましょう。テキストファイルへ書き込むにはモード「wt」、「xt」、「at」のいずれかでファイルを開きます。
いずれも存在しないファイルを開くと、新しくファイルが作成されます。これらの違いは既存のファイルを開くときです。「wt」なら既存のファイルをまず空にしてから書き込みます。「xt」なら例外FileExistsErrorが発生してオープンに失敗します。「at」ば既存のファイルの末尾から書き込みを始めます。
write()によるテキストファイルへの書き込み
ファイルへ書き込むテキストとして次の文字列を使います。
>>> text = '''海が光る ... 半分から ... こっちが光る ... '''
通常、ファイルへの書き込みにはモード「wt」を使います。
>>> fout = open('sample.txt', 'wt') >>> fout.write(text) 17 >>> fout.close()
open()関数はファイルのオープンに成功するとファイルオブジェクトを返します。
テキストファイルへの書き込みは、このはファイルオブジェクトのwrite()関数で行います。write()関数は書き込んだ文字数を返します。
すでに存在するファイルを「wt」で開くと元の内容が失われます。これが望んだことでなければ、開く前にos.path.exists()関数でファイルが存在するかどうかを確認します。
print()によるテキストファルへの書き込み
print()でテキストファイルに書き込むこともできます。
print()はデフォルトで標準出力(sys.stdout)へテキストを書き出しますが、引数fileへ任意のファイルオブジェクトを渡して、そこへ書き込むこともできます。
>>> fout = open('sample.txt', 'wt') >>> print(text, file=fout, end='') >>> fout.close()
ここでは最後に改行文字('\n')を追加して欲しくないので、「end=''」を指定しています。
print()はwrite()より高水準な出力関数です。複数のオブジェクトが渡されたときにセパレータ(引数sep)を間に挟んだり、最後に文字列(引数end)を追加することができます。
>>> print('one', 'two', 'three', sep=' ', end='\n') one two three
この例では説明のために明示的に「sep=' '」や「end='\n'」を渡していますが、これらはデフォルトなので省略しても結果は変わりません。これらの引数にお好みの文字列を指定して、デフォルトの動作を変更できます。
セパレータや末尾に文字列を追加したければwrite()の代わりにprint()を使うといいでしょう。
ファイルの上書きを禁止する
間違ってファイルを上書きしたくないならモード「x」を使ってファイルを開きます。
>>> fout = open('sample.txt', 'xt') Traceback (most recent call last): File "<stdin>", line 1, in <module> FileExistsError: [Errno 17] File exists: 'sample.txt'
ファイルが既に存在していればFileExistsErrorが発生し、ファイルのオープンに失敗します。
既存のファイルを上書きしないことを除けば、モード「w」で開いたときと使い方は同じです。
既存のテキストファイルへ追記する
既存のファイルにテキストを追記したいならモード「at」でファイルを開きます。
>>> fout = open('sample.txt', 'at') >>> print('これは追記テキスト', file=fout) >>> fout.close()
これは既存のテキストファイルの最後からテキストを書き込み始めます。
テキストファイからの入力
テキストファイルからファイルを読み込むにはモード「rt」でファイルを開きます。
テキストには行の概念があります。1行に1つのデータが保存されていることもよくあります。
テキストファイルから読み込むとき、このように行を読み込む方法がいくつか用意されています。
テキストファイルを一度に読み込む
最初はread()関数を使ってテキストファイルを読んでみましょう。read()関数を使うとファイルの内容をすべて一度に読み込みます。
>>> fin = open('sample.txt', 'rt') >>> content = fin.read() >>> fin.close() >>> content '海が光る\n半分から\nこっちが光る\n'
read()関数の引数を省略するとファイルの内容を一度にすべて読み込みます。これはサイズが大きいファイルを読み込む場合、大量のメモリを消費します。
一度に読み込む文字数を制限したければ、read()関数の引数に一度に読みだす文字数を渡すことができます。
>>> chunks = [] >>> fin = open('sample.txt', 'rt') >>> while True: ... chunk = fin.read(5) ... if not chunk: ... break ... chunks.append(chunk) ... >>> fin.close() >>> chunks ['海が光る\n', '半分から\n', 'こっちが光', 'る\n']
この例では一度に読み出すのは5文字です。これを繰り返し実施しています。
ファイルの内容をすべて読んだ後にread()関数を呼び出すと空文字列が返されます。
空文字列をifの条件で使うとFalseと評価されます。この例ではchunkが空文字列のとき、if文本体のbreakが実行されwhileのループを抜けます。
テキストファイルを1行ずつ読み込む
readline()関数を使うと、ファイルを1行ずつ読みだすことができます。readline()関数は呼び出されるごとに次の1行を返します。
>>> lines = [] >>> fin = open('sample.txt', 'rt') >>> while True: ... line = fin.readline() ... if not line: ... break ... lines.append(line) ... >>> fin.close() >>> lines ['海が光る\n', '半分から\n', 'こっちが光る\n']
ファイルの最後まで読んだ後にreadline()関数を呼び出すと、read()関数と同様に空文字列が返されます。
返された行の末尾の改行コード('\n')は残されます。ただし、ファイルの最後が改行コードで終わっていなければ、最後の行にはもちろん改行コードはありません。
for文でファイルオブジェクトを使う
ファイルオブジェクトはイテラブルなのでfor文で使うことができます。
for文で使うとreadline()と同様、繰り返すごとに次の1行が返されます。
>>> data = [] >>> fin = open('sample.txt', 'rt') >>> for line in fin: ... data.append(line.rstrip('\n')) ... >>> fin.close() >>> data ['海が光る', '半分から', 'こっちが光る']
最後の行まで読み込むとfor文を抜けます。
readline()関数と同様、返された行の末尾の改行コード('\n')は残されます。
この例では行末の改行コードをrstrip()を使って削除しています。
すべての行をリストに読み込む
readline()関数の例では1行ずつ読み出してリストに追加していました。本当にこのようなデータが欲しければreadlines()を使った方が簡単です。
>>> fin = open('sample.txt', 'rt') >>> lines2 = fin.readlines() >>> fin.close() >>> lines2 ['海が光る\n', '半分から\n', 'こっちが光る\n']
readlines()関数は1行ずつ読み込み、読み込んだ行をリストとして返します。
with文による自動的なファイルのクローズ
ファイルを開いたら必ず閉じる必要があることは前述しましたが、Pythonではこれを確実に行うための機構が用意されています。
それにはPythonのwith文を使います。with文を使うと最初の書き込みの例は次のように書くことができます。
>>> with open('relativity', 'wt') as fout: ... fout.write(poem) ...
open()関数をwith文で使うと、コンテキストマネージャが呼び出され、with文のブロックはそのランタイムコンテキストで実行されます。
と、このような難しい話はとりあえず置いておいて次の要点を押さえておきましょう。
- with文のopen()関数の呼び出しが成功すると、open()関数が返すファイルオブジェクトはasの後の変数(変数名は任意)へ代入される。
- with文本体のブロックでは、これまで通りファイルオブジェクトを使ってファイルを読み書きする。
- with文のブロックを抜けるとき、コンテキストマネージャの終了処理が実行される。これはwithブロックを正常に抜けるか、または例外によって抜けるかに関わらずファイルを必ず閉じます。
このようwith文を使うと、もはや明示的にclose()関数を呼び出す必要はなくなります。
引数newlineと引数encoding
これまでの例では引数newlineと引数encodingはopen()関数のデフォルトを使っていました。
テキストファイルを扱うには改行コードと文字コード(文字エンコーディング)を理解していないと、問題を引き起こすことがあります。
改行コードと文字エンコーディングを制御するのが引数newlineと引数encodingです。
引数newlineと改行コード
ここまで行末の改行コードについては触れて来ませんでした。ご存知かもしれませんが、UnixやMacではデフォルトの改行コードには'\n'が使われ、Windowsでは'\r\n'が使われています。
PythonでWindowsのテキストファイルを読み込んだとき、行の最後には'\r\n'という文字列が付くのではないか?と気になったかもしれません。
しかし、これまでの例ではテキストファイルの改行コードとして'\n'と'\r\n'のどちらが使われていいたとしても、読み込んだ行の末尾は必ず'\n'で終わります(ただし、最後の行は前述した通り'\n'が付かないことがあります)。
これはopen()関数の引数newlineが省略されているときのデフォルトの動作です。newline引数を省略すると、読み込んテキストの改行コード('\n'や'\r\n')は1文字の'\n'へ変換されます。
テキストファイルへ書き込むときにopen()関数の引数newlineを省略すると、反対の変換が行われます。書き込む文字列の'\n'は、システムデフォルトの改行コード(UnixやMacなら'\n'、Windowsなら'\r\n')へ変換されてからファイルへ書き込まれます。
このようなopen()関数のデフォルトの動作を使うと、実際のファイルの改行コードがなんであるか意識する必要はなく、プログラムでは常に改行コードが'\n'であるとして処理ができるという利点があります。
このデフォルトの動作を変更したい場合は、引数newlineに別の値を指定します。
これについては「Python3でファイルを読み書きするとき、改行コードはどうなるの?」に詳しく書きましたので参照してください。
引数encodingと文字コード(文字エンコーディング)
ファイルを開くときにファイルのエンコードやデコードするための文字エンコーディング(文字コードや文字符号化方式ともいう)を引数encodingに指定できます。よく使われる文字エンコーディングにはUTF-8やcp932などがあります。
引数encodingを省略するとOSデフォルトのエンコーディング(正確にはlocale.getpreferredencoding()が返すもの)が使われます。
特定の文字エンコーディングでファイルを読み書きするときは、引数encodingに指定します。指定できる文字エンコーディングはcodecsモジュールのドキュメントを参照してください。
なお、一般にWindowsでShift_JISを使うのは厳密には正しくありません。「Shift_JISとCP932とWindows-31Jの違いを整理した」の記事を読んでいただければ、この言葉の意味やcp932について理解いただけるでしょう。
バイナリファイルの入出力
バイナリファイルを読み書きするにはバイナリモード(b)でファイルをオープンします。
バイナリモードではbytesオブジェクトをバイナリファイルへ書き込み、バイナリファイルを読み込むとbytesオブジェクトが返されます。
バイナリファイルの書き込み
バイナリファイルへの書き込みには次のbytesオブジェクトを使います。
>>> bin = b'1234567890'
それでは実際に書き込んでみましょう。
>>> fout = open('sample', 'wb') >>> fout.write(bin) 10 >>> fout.close()
write()関数は書き込んだバイト数を返します。
そのほかのモード「xb」、「ab」での書き込みについては、テキストファイルの例を参考にしてください。
バイナリファイルの読み込み
次はバイナリファイルから読み込む例です。
>>> fin = open('sample', 'rb') >>> rbin = fin.read() >>> fin.close() >>> rbin b'1234567890'
read()関数はbytesオブジェクトを返します。
read()に正の整数を渡して一度に読み込むバイト数を制限できます。
>>> blist = [] >>> fin = open('sample', 'rb') >>> while True: ... rbin = fin.read(4) ... if not rbin: ... break ... blist.append(rbin) ... >>> fin.close() >>> blist [b'1234', b'5678', b'90']
ファイルの最後まで読んだ後にread()関数を呼び出すと、空のbytesオブジェクトが返されます。
まとめ
ここではファイル入出力の基本を説明するためwith文は使ってませんが、実際にコードを書くときはwith文を使うのがいいと思います。
またopen()関数はファイルオブジェクトを返すと説明しましが、実際に返されるファイルオブジェクトはテキストモード(t)とバイナリモード(b)で異なります。このようなことを調べていくとPythonのファイル入出力への理解が深まります。興味があれば調べてみてください。