見出し画像

Pythonをはじめよう!-14 入力と出力2

ファイルを読み書きする

open() は fileを開く時に使います。。使い方は、open(filename, mode) のように二つの引数を伴って呼び出されます。

>>> f = open('workfile', 'w')

最初の引数はファイル名(文字列)です。二つめの引数も文字列で、ファイルをどのように使うかを示す文字を入れ( mode)その文字としては、ファイルが読み出し専用なら 'r' 、書き込み専用 (同名の既存のファイルがあれば消去されます) なら 'w' とします。

 'a' はファイルを追記用に開きます。ファイルに書き込まれた内容は自動的にファイルの終端に追加されます。 'r+' はファイルを読み書き両用に開きます。 mode 引数は省略可能で、省略された場合には 'r' であると仮定します。

通常、ファイルはテキストモード (text mode) で開かれ、特定のエンコーディングでファイルに対して文字列を読み書きします。

モードに 'b' をつけるとファイルをバイナリモード (binary mode) で開き、 byte オブジェクトを読み書きします。テキストファイル以外のすべてのファイルはバイナリモードを利用しないといけません。

テキストモードの読み取りでは、自分の環境の行末記号 (Unix では \n 、Windows では \r\n) をただの \n に変換するのがデフォルトの動作です。テキストモードの書き込みでは、 \n が出てくる箇所を固有の行末記号に戻すのがデフォルトの動作です。

 JPEG ファイルや EXE ファイルのようなバイナリデータを破壊する恐れがあるので、バイナリモードを使うようにしてください。

ファイルオブジェクトを扱うときに with キーワードが便利です。 その利点は、処理中に例外が発生しても必ず最後にファイルをちゃんと閉じることです。 

try-finally ブロックで書けますが、with を使うと簡潔に書けます:

>>> with open('workfile') as f:
...     read_data = f.read()
>>> # We can check that the file has been automatically closed.
>>> f.closed
True

もし with キーワードを使用しない場合は、ファイルを閉じ、このファイルのために利用されたシステムのリソースを直ちに解放するために f.close() を呼び出す必要があります。

警告
f.write() を with キーワードや f.close() を使わずに呼び出した場合、プログラムが正常に終了した場合でも、 f.write() の実引数がディスクに完全に 書き込まれないことがあります 。

with 文や f.close() の呼び出しによって閉じられた後にファイルオブジェクトを使おうとするとそこで処理が失敗します。:

>>> f.close()
>>> f.read()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.

ファイルオブジェクトのメソッド

これからの話は、 "f" というファイルオブジェクトが既に生成されていること前提とします。

ファイルの内容を読み出すには、 f.read(size) を呼び出します。このメソッドはある量のデータを読み出して、文字列 (テキストモードの場合) か bytes オブジェクト (バイナリーモードの場合) として返します。 size はオプションの数値引数です。 size が省略されたり負の数であった場合、ファイルの内容全てを読み出して返します。ただし、ファイルのサイズが大きい場合は失敗する場合があります。

size が負でない数ならば、最大で (テキストモードの場合) size 文字、(バイナリモードの場合) size バイトを読み出して返します。ファイルの終端にすでに達していた場合、 f.read() は空の文字列 ('') を返します。

>>> f.read()
'This is the entire file.\n'
>>> f.read()
''

f.readline() はファイルから 1 行だけを読み取ります。改行文字 (\n) は読み出された文字列の終端に残ります。改行が省略されるのは、ファイルが改行で終わっていない場合の最終行のみです。これは、戻り値があいまいでないようにするためです; f.readline() が空の文字列を返したら、ファイルの終端に達したことが分かります。一方、空行は '\n'、すなわち改行 1 文字だけからなる文字列で表現されます。

>>> f.readline()
'This is the first line of the file.\n'
>>> f.readline()
'Second line of the file\n'
>>> f.readline()
''

ファイルから複数行を読み取るには、ファイルオブジェクトに対してループ処理を書くとメモリを効率的に使え、高速で、簡潔なコードになります:

>>> for line in f:
...     print(line, end='')
...
This is the first line of the file.
Second line of the file

ファイルのすべての行をリスト形式で読み取る場合は、list(f) や f.readlines() が使えます。

f.write(string) は、string の内容をファイルに書き込み、書き込まれた文字数を返します。

>>> f.write('This is a test\n')
15

オブジェクトの他の型は、書き込む前に変換が必要です-- 文字列 (テキストモード) と bytes オブジェクト (バイナリーモード) のいずれかです:

>>> value = ('the answer', 42)
>>> s = str(value)  # convert the tuple to string
>>> f.write(s)
18

f.tell() は、ファイルオブジェクトのファイル中における現在の位置を示す整数を返します。ファイル中の現在の位置は、バイナリモードではファイルの先頭からのバイト数で、テキストモードでは不明瞭な値で表されます。

ファイルオブジェクトの位置を変更するには、f.seek(offset, whence) を使います。ファイル位置は基準点 (reference point) にオフセット値 offset を足して計算されます。参照点は whence 引数で選びます。whence の値が 0 ならばファイルの 先頭から測り、1 ならば現在のファイル位置を使い、2 ならばファイルの終端を参照点として使います。whence は省略することができ、デフォルトの値は 0、すなわち参照点としてファイルの先頭を使います。

>>> f = open('workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5)      # Go to the 6th byte in the file
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2)  # Go to the 3rd byte before the end
13
>>> f.read(1)
b'd'

テキストファイル (mode 文字列に b を付けなかった場合) では、ファイルの先頭からの相対位置に対するシークだけが許可されています (例外として、seek(0, 2) でファイルの末尾へのシークは可能です)。また、唯一の有効な offset 値は f.tell() から返された値か、0 のいずれかです。それ以外の offset 値は未定義の振る舞いを引き起こします。

ファイルオブジェクトには、他にも isatty() や truncate() といった、あまり使われないメソッドがあります。

 json による構造化されたデータの保存

文字列は簡単にファイルに書き込んだり、ファイルから読み込んだりすることができます。数値の場合には少し努力が必要です。というのも、read() メソッドは文字列しか返さないため、int() のような関数にその文字列を渡して、たとえば文字列 '123' のような文字列を、数値 123 に変換しなくてはならないからです。もっと複雑なデータ型、例えば入れ子になったリストや辞書の場合、手作業でのパースやシリアライズは困難になります。

ユーザが毎回コードを書いたりデバッグしたりして複雑なデータ型をファイルに保存するかわりに、Python では一般的なデータ交換形式である JSON (JavaScript Object Notation) を使うことができます。この標準モジュール json は、Python のデータ 階層を取り、JSONデータに変換します。この処理は シリアライズ (serializing) と呼ばれます。JSONデータからデータを再構築することは、デシリアライズ (deserializing) と呼ばれます。

シリアライズすることで、オブジェクトのJSONデータはファイルやデータの形で保存したり、ネットワークを通じて離れたマシンに送ったりすることができます。

注釈
JSON 形式は現代的なアプリケーションでデータをやりとりする際によく使われます。

オブジェクト x があり、その JSON 形式のJSONデータを見るには、単純な1行のコードを書くだけです:

>>> import json
>>> x = [1, 'simple', 'list']
>>> json.dumps(x)
'[1, "simple", "list"]'

dumps() に似た関数に、dump() があり、こちらは単純にオブジェクトを text file にシリアライズします。f が書き込み用に開かれた text file だとすると、次のように書くことができます:

json.dump(x, f)

逆にデシリアライズするには、f が読み込み用に開かれた text file だとすると、次のようになります:

x = json.load(f)

このような単純なシリアライズをする手法は、リストや辞書を扱うことはできますが、任意のクラス・インスタンスを JSON にシリアライズするにはもう少し努力しなくてはなりません

参考

pickle - pickle モジュール

JSON とは対照的に、 pickle は任意の複雑な Python オブジェクトをシリアライズ可能なプロトコルです。しかし、Python に特有のプロトコルで、他の言語で記述されたアプリケーションと通信するのには使えません。さらに、デフォルトでは安全でなく、信頼できない送信元から送られてきた、スキルのある攻撃者によって生成された pickle データをデシリアライズすると、攻撃者により任意のコードが実行されてしまいます。

この記事が気に入ったらサポートをしてみませんか?