ファイルの読み書き

コンピュータシステムでは、計算結果を保存しておくためにファイル(ファイル)を使っている。 オペレーティングシステム(OS)にはファイル管理のためのファイルシステムがあり、それぞれのOSごとに定められた方法(システムコール(system call)によってファイルの作成やアクセスを提供している。 Pythonなどの高級言語ではファイルハンドラ(file handler)を通じてファイルにアクセスするように設計されているのでOSには依存しない。

Pythonでは、sys モジュールをインポートしたうえで、 ファイル操作 openwriteclose の3つのメソッドを使ったファイルのオープン、書き込み、クローズを行うことができる。

ファイルのクローズ操作は忘れがちだが大変重要だ。 書き込み操作によってファイルI/O(Input/Output)が即座に行われるわけではなく、メモリにバッファされているのでクローズしないとそれらは失われてファイル内容として保存されない。 また、ファイルが開かれるとファイルハンドル(変数)とファイルとが1対1に対応付けられ、他からはアクセスできないようになる(このような状態をロック)(lock)という)。 ファイルをクローズしないと、そのファイルをロックされたままで、他からオープンできなくなる。

現代のOSではマルチプロセス、マルチスレッドあるいは並列処理として、同時に複数のプログラムが進行している。 ある1つのファイル(たとえば、ある顧客の銀行口座情報としよう)の内容が矛盾なく管理されるためには、ファイルアクセスできる権利(トークン(token))を獲得しなければならない。 もし、ロックを取り合うような事態になればそれをデッドロック(deadlock)となる。

ファイル入出力のメソッド

次の表にファイルオブジェクトが持つ代表的なメソッドを紹介する。
ファイル入出力のメソッド
method名モード意味
opne(fname, mode)文字列 fname のファイルを mode にしたがって開き、ファイルハンドルを返す。
'r'文字列 fname のファイルを読み込み(read)モードで開き、ファイルハンドルを返す。ファイルが存在しないとエラー。
'w'文字列 fname のファイルを書き込み(write)モードで開き、ファイルハンドルを返す。読み込みはできない。同名の既存ファイルがあれば内容は消去される。
'a'文字列 fname のファイルを追加書き込み(append)モードで開き、ファイルハンドルを返す(読み込みはできない)。ファイルの参照点は自動的にファイルの末尾に移動。 ファイルがない場合は新規作成。
'a+'文字列 fname のファイルを追加書き込みモードで開き、ファイルハンドルを返す。参照点は自動的にファイルの末尾に移動。ファイルがない場合は新規作成。
'r+'文字列 fname のファイルを読み書き両用に開き、ファイルハンドルを返す。ファイルがない場合はエラー。
'w+'読み書き両用。ファイルがある場合は'w' と同じ処理。 文字列 fname のファイルを空にして、そのファイルを書き込み用に開く。
't'テキストモード(デフォルト)
'b'バイナリモードで開く(他のオプションと併せて指定)
fh.read()ファイルハンドル fh が関連付けられているファイルのファイル終端まで全て読んだデータを返す)。
fh.readline()ファイルハンドル fh が関連付けられているファイルの次の行を返す(行単位での読み込み)。
fh.readlines()ファイルハンドル fh が関連付けられているファイルの各1行を要素とするリストを返す。
fh.write(str)ファイルハンドル fh が関連付けられているファイルに、文字列のシークエンス sequenceの各要素を書き込む。 シーケンスは文字列を生成する反復可能なオブジェクトなら何でもよい(文字列からなるリストなど)
fh.read(n)バイナリモードにおいてファイルのデータをnバイト分読み込む。
fh.close()ファイルハンドル fh が関連付けられているファイルを閉じる。

代表的ファイル操作

コマンドラインでファイル名を指定する

スクリプト起動時に、コマンドライン引数で指定されたファイルを開きたいことが多い。 次のスクリプト cat.py では、スクリプト起動時にテキストファイル名を指定して、その内容を表示するスクリプトである。 4行目で sys モジュールをインポートして、6行目で第1引数で渡された文字列 sys.argv[1] をファイル名とするファイルをファイルハンドル fp として読み込み専用モードで開いている(第0引数 sys.argv[0] にはスクリプト名名が格納されている)。

コマンドラインで渡されファイルが存在すれば(ファイル名にはスクリプトが動いている作業ディレクトリ/フォルダから目的のファイルへのファイルパスを含んでいてもよい)、readモードでファイルが開かれ、ファイルハンドル fh に紐付けられる(6行目)。 7行目では、メソッド readline() により、ファイル内の各行がリスト要素として並べられ、各要素がfor文のカウンタ変数 line に代入される。 8行目で、line変数の文字列を書き出している。 書き出して改行しないようにするためにlineの後にカンマ(,) があることに注意。 12行目で、ファイルをクローズすることを忘れないように。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

fh = open(sys.argv[1], 'r')
for line in fh.readlines():
    print line,
#for line in fh:
#    print line[:-1]

fh.close()

[実行例] 1行目でスクリプト cat.py に引数として 自分自身 cat.py を渡してその内容を表示している(コマンド引数は空白で区切られている必要がある。ファイル名に空白文字を入れないなどの配慮はこのような事情があるためだ)。

$ ./cat.py cat.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

fh = open(sys.argv[1], 'r')
for line in fh.readlines():
    print line,
#for line in fh:
#    print line[:-1]

fh.close()

上のスクリプト cat.py で、8行目

演習: スクリプト cat.py を実行してみなさい。 8行目の line の後のカンマ(,)を亡くして実行したらどうなるか。 また、8,9行をコメントアウト(#記号を行頭に)し、10,11行のコメントを外して(#記号を除去)実行してみなさい。

キーボード入力をファイルに書き出す

次のスクリプト write_keyinputs.py はキー入力された文字を 'e' または 'E' が入力されるまで繰り返しファイル key_inputs.txt に書き出していく。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

fh = open('key_inputs.txt', 'w')

number = raw_input("input number or friend's name('e'/'E' for finish): ")
while not (number == 'e' or number == 'E'):
    fh.write(number)
    number = raw_input("input number or friend's name('e'/'E' for finish): ")

fh.close()
演習: 次のスクリプト write_keyinputs.py を実行してみなさい。 数字や名前などを入力正常終了した後に、そのファイル内容を表示しなさい。 思うような結果が得られたかを検討しなさい。

疑似乱数を書き出す

ファイルの入出力においては、基本はテキストでのやり取りと言うことに注意する。 次のスクリプト write_num_file.py は random モジュールをインポートして、-2と5の間の一様疑似乱数を浮動小数点として 100 個生成して、その値をファイル random_numbers.txt に書き出すものである。

8行目の二箇所に注意しよう。 random.uniform(-2,5) で生成した浮動小数点を str 関数で文字列に変更し、さらに、+ '\n' で改行文字列を連接してからファイルに書き出している。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import random

fh = open('random_numbers.txt', 'w')
for n in range(100):
    fh.write(str(random.uniform(-2,5)) + '\n')

fh.close()
演習: 上のスクリプト write_keyinputs.py において、fh.write(str(random.uniform(-2,5)) + '\n') とぜずに、fh.write(random.uniform(-2,5)) とした場合にどうなるかを確かめなさい。 さらに、fh.write(str(random.uniform(-2,5))) と '\n' がない場合のファイル内容を確認しなさい。

ファイル内容を読み出して数字として利用する場合は以下のような注意が必要だ。

スクリプト write_num_file.py で書き出されたファイル random_numbers.txt の内容を読み出すスクリプトが次の read_num_file.py である。 7.8行がポイントである。 8行目では、fh.readlines() でファイルの各行にある文字列を要素とするリストをカウンタ変数 number として取り出している。 9行目では、取り出したテキスト(!)を関数 float で浮動小数としてから3倍してプリントしている。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import random

fh = open('random_numbers.txt', 'r')
for number in fh.readlines():
    print 3 * float(number)

fh.close()
演習: 上のスクリプト read_num_file.py において、print 3 * float(number) とぜずに、print 3 * number とした場合にどうなるかを確かめなさい(エラーとはならない!)。