データを恒久的に保存するために最もよく使われるのはファイルです。Pythonはデータをファイルへ保存したり、データをファイルから読み込むことが簡単できます。
プログラムを書くうえで、ファイル入出力はなくてはならないものです。ここではPythonのファイル入出力の方法を説明します。
ファイルのオープンとクローズ
Pythonでファイルの入出力を行うには、まずファイルをオープン(開く)する必要があります。ファイルを使い終わったらファイルをクローズ(閉じる)します。
ファイルをオープンする
ファイルをオープンするには組み込みのopen()関数を使います。open()関数の基本的な呼び出しは次のようなものです。
fileobj = open(filename, mode)
filename引数にはファイル名(あるいは絶対パスか相対パス)を文字列で渡します。mode引数にはファイルの使用用途を示す文字列を渡します。ファイルのオープンに成功するとopen()関数はファイルオブジェクトを返します。ファイルをオープンできなければOSErrorが送出されます。
mode引数には、用途を示す2種類の文字を指定します。1つ目はファイルの読み書きの用途を示す文字です。次のいずれかを指定します。
1文字目 | 意味 |
---|---|
r | 読み取り用にオープンする。 |
w | 書き込み用にオープンする。ファイルが存在しない場合、新しいファイルが作成される。既存ファイルを開くと、まずファイルが空にされます。 |
x | 書き込み用にオープンする。ファイルが存在しない場合、新しいファイルが作成される。ファイルが存在する場合はFileExistsErrorが発生する。 |
a | 書き込み用にオープンする。ファイルが存在しない場合、新しいファイルが作成される。ファイルが存在する場合は末尾に追記する。 |
もう1つはテキストかバイナリかを示す次の文字のいずれかです。
2文字目 | 意味 |
---|---|
t | テキストモード(テキストファイルを開くとき) |
b | バイナリモード(バイナリファイルをを開くとき) |
デフォルトでファイルはテキストモードでオープンされるので、テキストモードで開きたいときは「t」を省略できます。またmode引数自体を省略すると「r」が指定されたものと見なされます(つまり、テキストモードで読み取り専用で開かれる)。
したがって、読み取り用・テキストモードでファイルをオープンするとき、以下の記述はすべて同じです.
fileobj = open('example.txt', 'rt')
fileobj = open('example.txt', 'r')
fileobj = open('example.txt')
open()関数の呼び出しが成功すると、ファイルオブジェクトが返されます。ファイルへの読み書きは、このファイルオブジェクトのメソッドで行います。
ファイルをクローズする
ファイルを使い終えたら、ファイルをクローズします。ファイルのオープンにはOSの限りあるリソースを使います。ファイルをクローズするとそのリソースが解放されます。そのため必要なくなったら速やかにファイルをクローズするのがベストプラクティスです。
ファイルをクローズするにはファイルオブジェクトのclose()メソッドを使います。
fileobj.close()
書き込み関数でファイルへ書き込んでも、それがすぐにディスクへ書き込まれると考えてはいけません。close()関数を呼び出すと、書き込んだ内容がディスクへも書き込まれることが保証されます。
with文による自動的なファイルのクローズ
ファイルを開いたら閉じる必要があることは前述しましたが、Pythonではこれを確実に行うためwith文が用意されています。with文は次のように書きます。
>>> with open('text.txt', 'w') as f:
... ここに処理
with文で使うと、コンテキストマネージャが呼び出され、with文のブロックはそのランタイムコンテキストで実行されます。と、このような難しい話はとりあえず置いておいて次の要点を押さえておきましょう。
- with文のopen()関数の呼び出しが成功すると、open()関数が返すファイルオブジェクトはasの後の変数(変数名は任意)へ代入される。
- with文のブロック本体では、ファイルオブジェクトを使って読み書きする。
- with文のブロックを抜けるとき、コンテキストマネージャの終了処理が実行される。これはwithブロックを正常に抜けるか、または例外によって抜けるかに関わらずファイルを必ず閉じます。
このようwith文を使うと、明示的にclose()関数を呼び出す必要なく、ファイルのクローズをPythoに任せることができます。
テキストファイルの入出力
テキストファイルは文字で構成されます。テキストには行があり、各行は改行コードで終わります。そしてテキストファイルは特定の文字エンコーディング(UTF-8など)でエンコードされていいます。これらはバイナリファイルにはないテキストファイルの特徴です。
テキストファイルは、テキストモードでファイルをオープンします。open()関数は、デフォルトでテキストモードでオープンします。
ここではテキストファイルの読み書き方法を説明します。テキストファイル特有の文字エンコーディングと改行コードについては後で説明します。
テキストファイを読み込む
ここでの例では、次の内容のsample.txtを読み込みます。
line 1
line 2
line 3
テキストファイルを一度に読み込む — read()
最初はread()メソッドを使ってテキストファイルを読んでみましょう。ファイルオブジェクトのread()メソッドを使うとファイルの内容をすべて一度に読み込むことができます。
>>> fin = open('sample.txt')
>>> content = fin.read()
>>> content
'line 1\nline 2\nline 3\n'
read()メソッドの引数を省略するとファイルの内容を一度にすべて読み込みます。これはサイズの大きいファイルを読み込む場合、大量のメモリを消費するので注意してください。
一度に読み込む文字数を制限したければ、read()メソッドに一度に読みだす文字数を引数として渡します。この例ではwith文を使っています。
>>> with open('sample.txt') as fin:
... while True:
... chunk = fin.read(12)
... if not chunk:
... break
... print('Read:')
... print(chunk)
...
Read:
line 1
line
Read:
2
line 3
ファイルの内容をすべて読んだ後にread()メソッドの呼び出すと、空文字列が返されます。この例でread()が空文字を返すとif文の条件式が真となり、breakでループを抜けてプログラムは終了します。
テキストファイルを1行ずつ読み込む — readline()
readline()メソッドを使うと、ファイルを1行ずつ読みだすことができます。readline()メソッドは呼び出されるごとに、次の1行を返します。
>>> lines = []
>>> with open('sample.txt') as fin:
... while True:
... line = fin.readline()
... if not line:
... break
... lines.append(line)
...
>>> lines
['line 1\n', 'line 2\n', 'line 3\n']
ファイルを最後まで読んだ後にreadline()メソッドを呼び出すと、read()と同様に空文字列が返されます。
返された行の末尾の改行コード(\n)は残されます。ただし、ファイルの最後が改行コードで終わっていなければ、最後の行にはもちろん改行コードはありません。末尾の改行コードが不要な場合は、文字列オブジェクトのrstrip()メソッドなどを使って取り除きます。
for文でファイルオブジェクトをイテレートする
ファイルオブジェクトはイテラブルなのでfor文で使うことができます。for文で使うとイテレートするたびに、readline()と同様に次の1行が返されます。
先ほどの例をfor文を使って書き換えたのが以下のコードです。ただし、この例では行末の改行文字を削除する処理を追加しています。
>>> lines = []
>>> with open('sample.txt') as fin:
... for line in fin:
... lines.append(line.rstrip('\n'))
...
>>> lines
['line 1', 'line 2', 'line 3']
ファイルのすべての行をイテレートするとfor文を抜けます。このように行ごとに処理する場合はfor文を使ったほうがシンプルです。
テキストファイルの行を一度に読み込む — readlines()
readline()メソッドの例では1行ずつ読み出してリストに追加していました。本当にこのようなデータが欲しければreadlines()メソッドを使った方が簡単です。
readlines()メソッドは、各行を要素とするリストを返します。
>>> lines = []
>>> with open('sample.txt') as fin:
... lines = fin.readlines()
...
>>> lines
['line 1\n', 'line 2\n', 'line 3\n']
テキストファイルへの出力
テキストファイルへ書き込むには、open()関数のmode引数に「wt」、「xt」、「at」のいずれかを指定してファイルをオープンします(「t」は省略可能)。
いずれのモードでも、存在しないファイルを開くと新しくファイルが作成されます。
これらのモードの違いは既存のファイルを開くときです。「wt」だと既存のファイルをまず空にするので、ファイルを上書きするときに使います。「xt」ならオープンに失敗し、例外FileExistsErrorを送出します。「at」ば既存のファイルの末尾に追記する場合に使います。
テキストファイルへの書き込み — write()、print()
ここでの例で、ファイルへ書き込むテキストとして次の文字列を使います。
>>> text = '''海が光る
... 半分から
... こっちが光る
... '''
最初にopen()関数のmode引数に「w」を渡してオープンする例です。テキストファイルへの書き込みには、通常write()メソッドを使います。write()メソッドは書き込んだ文字数を返します。
>>> fout = open('sample.txt', 'w')
>>> fout.write(text)
17
>>> fout.close()
すでに存在するファイルを「w」でオープンすると元の内容が失われます。ファイルが存在するかはos.path.exists()関数で確認できます。
書き込んだ後のsample.txtファイルは次のとおりです。
海が光る
半分から
こっちが光る
print()関数でテキストファイルに書き込むこともできます。print()関数はデフォルトで標準出力(sys.stdout)へ書き込みますが、file引数に任意のファイルオブジェクトを受け入れますので、ファイルオブジェクト渡すとそこへ書き込みます。
>>> fout = open('sample.txt', 'w')
>>> print(text, file=fout, end='')
>>> fout.close()
print()関数はデフォルトで最後に改行文字を出力するので、ここではその改行文字出力しないように「end=”」を指定しています。
print()関数はwrite()メソッドより高水準な出力関数です。複数のオブジェクトが渡されたときにセパレータ(sep引数)を間に挟んだり、最後に文字列(end引数)を追加することができます。
>>> print('one', 'two', 'three', sep=' ', end='\n')
one two three
この例では説明のために明示的に「sep=’ ‘」や「end=’\n’」を渡していますが、これらはデフォルトなので省略しても結果は変わりません。これらの引数にお好みの文字列を指定して、デフォルトの動作を変更できます。セパレータや末尾に文字列を追加したいときはprint()関数が便利です。
ファイルのクローズを保証するために、with文を使うと次のようになります。
>>> with open('sample.txt', 'w') as fout:
... fout.write(text)
...
17
ファイルの上書きを禁止する
間違ってファイルを上書きしたくないなら、mode引数に「x」を渡してファイルをオープンします。
>>> fout = open('sample.txt', 'x')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileExistsError: [Errno 17] File exists: 'sample.txt'
ファイルが既に存在していればファイルのオープンに失敗し、FileExistsErrorを送出します。
既存ファイルはオープンできないことを除けば、mode引数「w」でオープンしたと使い方は同じです。
既存のテキストファイルへ追記する
既存のファイルにテキストを追記したいならmode引数に「a」を渡してでファイルをオープンします。
>>> with open('sample.txt', 'a') as fout:
... print('追記テキスト', file=fout)
...
これはファイル末尾から書き込みを始めます。書き込んだ結果は次のとおりです。
海が光る
半分から
こっちが光る
追記テキスト
newline引数とencoding引数
これまでの例ではopen()関数のnewline引数とencoding引数はデフォルトを使っていました。
テキストファイルを扱うには改行コードと文字エンコーディング(文字コード)を理解していないと、プログラムが思わぬ結果になることがあります。改行コードと文字エンコーディングを制御するのがnewline引数とencoding引数です。
newline引数と改行コード
ここまで行末の改行コードについては触れて来ませんでした。ご存知かもしれませんが、UnixやMacではデフォルトの改行コードには’\n’が使われ、Windowsでは’\r\n’が使われています。
PythonでWindowsのテキストファイルを読み込んだとき、行の最後には’\r\n’という文字列が付くのではないか?と気になったかもしれません。
しかし、これまでの例ではテキストファイルの改行コードとして’\n’と’\r\n’のどちらが使われていいたとしても、読み込んだ行の末尾は必ず’\n’で終わります。
これはopen()関数のnewline引数が省略されているときのデフォルトの動作です。newline引数を省略すると、読み込んテキストの改行コード(’\n’や’\r\n’など)がなんであれ、1文字の’\n’へ変換されます。
テキストファイルへ書き込むときにopen()関数のnewline引数を省略すると反対の変換が行われます。書き込む文字列の’\n’は、システムデフォルトの改行コード(UnixやMacなら’\n’、Windowsなら’\r\n’)へ変換されてからファイルへ書き込まれます。
このようなopen()関数のデフォルトの動作を使うと、実際のファイルの改行コードがなんであるか意識する必要はなく、プログラムでは常に改行コードが’\n’であるとして処理ができるという利点があります。
このデフォルトの動作を変更したい場合は、newline引数に別の値を指定します。
newline引数に渡せる値、およびそのときの動作については以下の記事に詳しく書いてありますので参照してください。
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')
>>> b = fin.read()
>>> fin.close()
>>> b
b'1234567890'
read()メソッドはbytesオブジェクトを返します。
read()メソッドに正の整数を渡して一度に読み込むバイト数を制限できます。
>>> with open('sample', 'rb') as fin:
... while True:
... b = fin.read(4)
... if not b:
... break
... blist.append(b)
...
>>> blist
[b'1234', b'5678', b'90']
ファイルの最後まで読んだ後にread()メソッドを呼び出すと、空のbytesオブジェクトが返されます。
pathlibモジュールを使ったファイルの入出力
pathlibモジュールの具象パスクラスにもファイルをオープンしたり、ファイルを読み書きするためのメソッドが定義されています。
具象パスクラスのopenメソッド
>>> p = Path('sample.txt')
>>> with p.open('w') as fout:
... fout.write('sample contents\n')
...
12
まとめ
open()関数はファイルオブジェクトを返すと説明しましが、実際に返されるファイルオブジェクトはテキストモード(t)とバイナリモード(b)で異なります。このようなことを調べていくとPythonのファイル入出力への理解が深まります。興味があれば調べてみてください。