ファイルの読み書き

コンピュータシステムでは、計算結果を保存しておくためにファイル(ファイル)を使っている。 オペレーティングシステム(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 に紐付けられる(3行目)。 4行目では、メソッド readline() により、ファイル内の各行がリスト要素として並べられ、各要素がfor文のカウンタ変数 line に代入される。 5行目で、line変数の文字列を書き出している。 書き出して改行しないようにするために、Python3に従って lineの後に改行文字無しにしていることに注意。 9行目で、ファイルを開いておく必要長くなった段階でクローズすることを忘れないように(複数のファイルを取り扱うときなど大変重要な心遣いである)。

import sys

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

fh.close()

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

$ python cat.py cat.py
演習: スクリプト cat.py を実行してみなさい。 5行目で line の後の『, end = ''』を無くして保存、実行したらどうなるか確認しなさい。 また、4, 5行をコメントアウト(#記号を行頭に)し、6,7行のコメントを外して(#記号を除去)実行したらどうなるかて試してみなさい(その理由を說明しなさい)。

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

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

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

str = raw('input something('Enter/Return for finish)= ')
while str != '':
    fh.write(str)
    str = input('input something('Enter/Return for finish)= ')

fh.close()
演習: 次のスクリプト write_keyinputs.py を実行してみなさい。 数字や名前などを入力正常終了した後に、生成されたファイル内容をエディタで確認してみなさい。 思うような結果が得られたか、改良すべき点はないかも検討しなさい。

疑似乱数を書き出す

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

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

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)) とした場合にどうなるかを確かめ、書き出されたファイル random_numbers.txt の内容を確認しなさい。

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

スクリプト write_num_file.py で書き出したファイル random_numbers.txt の内容を読みこんで数字として利用するスクリプトが次の read_num_file.py である。 4, 5行がポイントである。 4行目では、fh.readlines() でファイルrandom_numbers.txtの各行にある文字列を要素とするリストをカウンタ変数 st として取り出している。 5行目では、取り出した文字列を浮動小数として関数 float によって型変換してから、3倍した結果をプリントしている。

import random

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

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