今さらPython3 (49) - テキストファイル入出力

第8章に入りました!130ページある付録を除けば、後半戦に突入ですね。

入門 Python 3

入門 Python 3

知らなかったのか、ドキュメントを読んでないからか

これまでも、fileを読み込んでPythonで何かを処理をするという経験はあるんだけど、あまり意味が分からずにおまじないのように決められた通りに入れていただけ。今度こそ、内容を理解しよう。

fileobj = open(filename, mode)

ま、記憶があやふやなのは、modeのところだよね。

  • r: read読み込み
  • w: write書き込み
  • x: exclusive排他的な生成
  • a: append追記
  • t: textテキスト
  • b: binaryバイナリ

とりあえず実験して覚えるその前に、ファイルの読み書きにあたり、いったんPythonをquit()して、ファイルを書き出しても構わないフォルダにcdコマンドで移動しておいた方が良いよね。移動したらPythonを再起動しておく。

>>> quit()
$ cd /Users/xxxxxx/PycharmProjects/IntroducingPython3
$ pwd
/Users/xxxxxx/PycharmProjects/IntroducingPython3
$ python3
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 23 2015, 02:52:03) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 

ルートとかに訳の分からんゴミファイル(勉強している今は大事かも知れないけど、将来的には紛れもないゴミファイルになるから)があるのは美しくないので。

書き出し

>>> poem = '''There was a young lady named Bright,
... WHose speed was far faster than light;
... She started one day
... In a relative way,
... And returned on the previous night.'''
>>> len(poem)
150
>>>
>>> fout = open('relativity', 'wt')
>>> fout.write(poem)
150
>>> fout.close()
>>>  

念のため、いったんquit()してファイルができているか確認。大丈夫そうだね。

$ ls -l
total 32
drwxr-xr-x  3 xxxxxx  staff  102 Dec 19 12:44 __pycache__
-rw-r--r--  1 xxxxxx  staff  150 Dec 22 16:21 relativity
-rw-r--r--  1 xxxxxx  staff   40 Dec 18 08:24 test1.py
-rw-r--r--  1 xxxxxx  staff   49 Dec 18 08:29 test2.py
-rw-r--r--  1 xxxxxx  staff   41 Dec 19 12:35 zoo.py

writeの代わりにprintでも行けるという話。

>>> poem = '''There was a young lady named Bright,
... Whose speed was far faster than light;
... She started one day
... In a relative way,
... And returned on the previous night.'''
>>> fout = open('relativity', 'wt')
>>> print(poem, file=fout)
>>> fout.close
<built-in method close of _io.TextIOWrapper object at 0x100387ea0>
>>> fout.close()
>>> 

以下のようなパラメータを渡すと、printでもwriteと同じ動きになる。

>>> fout = open('relativity', 'wt')
>>> print(poem, file=fout, sep='', end='')
>>> fout.close()
>>> 

いちおう、ファイルを見比べているけど、違いがよく分からん。

>>> fout = open('relativity', 'wt')
>>> size = len(poem)
>>> offset = 0
>>> chunk = 100
>>> while True:
...     if offset > size:
...         break
...     fout.write(poem[offset:offset+chunk])
...     offset += chunk
... 
100
50
>>>
>>> fout.close() 

これは、100文字ごとに分割してファイルに書き込んでいるパターン。データサイズが大きい場合のオプションになり得ると。

>>> fout = open('relativity', 'xt')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileExistsError: [Errno 17] File exists: 'relativity'
>>> 

xオプションを使っているので、ファイルが存在しない場合のみ実行されるので上書きを防げる。

>>> try:
...     fout = open('relativity', 'xt')
...     fout.write('stomp stomp stomp')
... except FileExistsError:
...     print('relativity already exits! That was a close one.')
... 
relativity already exits! That was a close one.
>>> 

例外ハンドリングもできるね。

読み出し

>>> fin = open('relativity', 'rt')
>>> poem = fin.read()
>>> len(poem)
150

ファイルを読むときの基本形はこれ。サイズが分からないときは開くのが怖い。そりゃそうだ。

>>> poem = ''
>>> fin = open('relativity', 'rt')
>>> chunk = 100
>>> while True:
...    fragment = fin.read(chunk)
...    if not fragment:
...        break
...    poem += fragment
... 
>>> fin.close()
>>> len(poem)
150
>>> 

これで動くということは、read文は前回読み出したところまで覚えているってことだね。

>>> poem = ''
>>> fin = open('relativity', 'rt')
>>> while True:
...     line = fin.readline()
...     if not line:
...        break
...     poem += line
... 
>>> fin.close()
>>> len(poem)
150
>>> 

1行ずつ読むreadline()の方がしっくりくるかな。1行ずつ読んで、データ構造に受け渡すみたいな。

>>> poem = ''
>>> fin = open('relativity', 'rt')
>>> for line in fin:
...     poem += line
... 
>>> fin.close()
>>> len(poem)
150
>>> 

forで回すのは楽だけど、どんな仕組みか気になったので、少しだけ深掘り。

>>> fin = open('relativity', 'rt')
>>> fin
<_io.TextIOWrapper name='relativity' mode='rt' encoding='UTF-8'>
>>> print(fin)
<_io.TextIOWrapper name='relativity' mode='rt' encoding='UTF-8'>
>>> type(fin)
<class '_io.TextIOWrapper'>
>>> fin[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '_io.TextIOWrapper' object is not subscriptable
>>> for line in fin:
...     print(line)
... 
There was a young lady named Bright,

Whose speed was far faster than light;

She started one day

In a relative way,

And returned on the previous night.
>>> 

別にリストとして格納されている訳ではなく、実際には1行ずつ読まれているんだろうな。

>>> fin = open('relativity', 'rt')
>>> lines = fin.readlines()
>>> fin.close()
>>> print(len(lines), 'lines read')
5 lines read
>>> for line in lines:
...     print(line, end='')
... 
There was a young lady named Bright,
Whose speed was far faster than light;
She started one day
In a relative way,
And returned on the previous night.>>> 

print()でend=''と指定しているので、改行が詰められた感じで出力されている訳ですね。readline()とreadlines()は1行ずつ読むか、まとめて読むかの違いがあり、readlines()で取得したlines変数の中身を見てみると、

lines
['There was a young lady named Bright,\n', 'Whose speed was far faster than light;\n', 'She started one day\n', 'In a relative way,\n', 'And returned on the previous night.']

リストになってる。

(つづく)