-Python- ファイル操作

ファイルを開くには,open関数を使います.次のコードでは,テキストファイルを開いてその中身を1行ずつ標準出力に出力します.

例として,以下のようなtest.txtというテキストファイルがあったとします.

0.0,0.0,0.0014294350166200182,-0.001270499567236085,0.0006352497836180425,-0.001270499567236085
-0.0004764783388733394,0.0,0.0004764783388733394,0.0,0.001270499567236085,0.0
-0.0004764783388733394,-0.0004764783388733394,-0.0009529566777466788,0.0,0.0019057493508541274,0.001270499567236085
0.0,-0.0004764783388733394,0.0,-0.001270499567236085,0.0006352497836180425,-0.00254099913447217
-0.0009529566777466788,-0.0009529566777466788,-0.0014294350166200182,0.0,-0.001270499567236085,-0.0019057493508541274

その上で,下記のスクリプト(sample.py)を作成します.

f = open("test.txt")
for line in f:
    line = line.rstrip()
    print(line)

 

f.close()
 
このスクリプトはテキストファイルを開いて,その中身を一行づつ標準出力(モニタ)に出力するものです.
まずは,ファイル(test.txt)をテキストファイルとして読み込み専用で開,変数 f にはファイルオブジェクトが代入されます.for文のinの後にファイルオブジェクトがくる場合は,対応するファイルに対して,各行を読み取りながらループするという意味になります.各行は変数lineに格納されますが,そこには改行文字も含まれているので,rstripメソッドによって改行文字を除去しています.改行文字を除去した後に,print関数で表示しています(ここでは,自動的に改行がされる).最後にcloseメソッドを読んでファイルを閉じてリソースを開放しています.
なお,このtest.txtファイルは元々csv形式(後述)なので,カンマで区切られています.
 
実行結果は以下のようになります.
$ python sample.py
0.0,0.0,0.0014294350166200182,-0.001270499567236085,0.0006352497836180425,-0.001270499567236085
-0.0004764783388733394,0.0,0.0004764783388733394,0.0,0.001270499567236085,0.0
-0.0004764783388733394,-0.0004764783388733394,-0.0009529566777466788,0.0,0.0019057493508541274,0.001270499567236085
0.0,-0.0004764783388733394,0.0,-0.001270499567236085,0.0006352497836180425,-0.00254099913447217
-0.0009529566777466788,-0.0009529566777466788,-0.0014294350166200182,0.0,-0.001270499567236085,-0.0019057493508541274
 
なお,上記の例で,line = line.rstrip()を記載しないと,実行結果は以下のようになって,改行が実行されてしまいます.

$ python sample.py
0.0,0.0,0.0014294350166200182,-0.001270499567236085,0.0006352497836180425,-0.001270499567236085
 
-0.0004764783388733394,0.0,0.0004764783388733394,0.0,0.001270499567236085,0.0
 
-0.0004764783388733394,-0.0004764783388733394,-0.0009529566777466788,0.0,0.0019057493508541274,0.001270499567236085
 
0.0,-0.0004764783388733394,0.0,-0.001270499567236085,0.0006352497836180425,-0.00254099913447217
 
-0.0009529566777466788,-0.0009529566777466788,-0.0014294350166200182,0.0,-0.001270499567236085,-0.0019057493508541274
 
上の例では,最後に明示的にファイルを閉じていますが,ファイルを開いてから閉じるまでの間に例外的に処理を抜け出してしまい,closeメソッドが呼ばれない可能性があります(その場合はファイルは開きっぱなしになる).そのような問題はwith構文を使うことで解消されます.
 
with open("test.txt") as f:
    for line in f:
        line = line.rstrip()
 
        print(line)
 
実行結果は以下のようになります.
$ python sample2.py
0.0,0.0,0.0014294350166200182,-0.001270499567236085,0.0006352497836180425,-0.001270499567236085
-0.0004764783388733394,0.0,0.0004764783388733394,0.0,0.001270499567236085,0.0
-0.0004764783388733394,-0.0004764783388733394,-0.0009529566777466788,0.0,0.0019057493508541274,0.001270499567236085
0.0,-0.0004764783388733394,0.0,-0.001270499567236085,0.0006352497836180425,-0.00254099913447217
-0.0009529566777466788,-0.0009529566777466788,-0.0014294350166200182,0.0,-0.001270499567236085,-0.0019057493508541274
 
with構文を使ってファイルがopenされていて,そのスコープ内にforループがあります.この場合,想定外の理由でwithスコープを抜け出したとしても,そのタイミングでファイルが閉じられます.つまり,closeメソッドで明示的にファイルを閉じる場合は,途中で例外が怒ってそのcloseを実行せずに通過してしまう可能性を気にしなければならないので,with構文を使った方が安心かつ便利です.
 
ファイルに書き込みを行う場合には,2つ目の引数として"w"を指定します.読み込み用にファイルを開くときの第2引数は"r"ですがこれはデフォルト値なので,記載する必要はありません.追記用に開く時には"a"を指定します.また,バイナリファイルを開く場合には,第2引数に"b"を付記します.バイナリファイルを読み込み用に開く場合は"rb"とします.
 
with open("output.txt", "w") as fw:
    with open("test.txt") as fr:
        for line in fr:
            print(line, end = "", file = fw)
 
この例では,"output.txt"というファイルを書き込み用に開いて,test.txtから1行ずつ読み取って,書き込み用ファイルに書き込んでいます.rstripを使って改行を削除する代わりに,print関数の名前付き引数endに''"を指定して,書き込み時に改行を付加しないようにしています.また,名前付き引数 file は書き込むべきファイルオブジェクトを意味します.
 
pickleモジュールの利用
オブジェクトの中身をファイルに書き出して保存して,必要に応じてそれを読み込み,元のオブジェクトの復元を行いたい場合は,pickleモジュールを使用します.いかに使用例を示します.
>>> from datetime import date
>>> import pickle
>>> x = date(2011, 1, 1)
>>> x
datetime.date(2011, 1, 1)
>>> with open("today.pkl", "wb") as f: 
...     pickle.dump(x, f, -1)
... 
>>> with open("today.pkl", "rb") as f: 
...     y = pickle.load(f)
... 
>>> y
datetime.date(2011, 1, 1)
 
pickle形式でファイルに書き込むには,dump関数を使いますが,その前にファイルを書き込み用に開いている必要があります.この時開くファイルは,テキスト形式でもバイナリ形式でも構いません(バイナリ形式の方が実行速度が速い).
dump関数の引数には,書き込みたいオブジェクト,書き込みフォーマットのバージョンを指定します.最後の引数については,-1を指定すると最新バージョンが適用されてパフォーマンスが良くなるので,理由が特にない場合は"-1"を指定します.
pickle形式で保存されたファイルを開く時には,ファイルを開いた上で,load関数を用います.バイナリ形式で保存されたファイルを読み込むにはバイナリ形式を指定する必要があります.
 
その他のファイル形式(csv, json
ここまでの説明では,テキストファイルを対象とした説明を行ってきていますが,データの処理では他の形式のファイルも用いられます.

まずは,csv形式のファイルについて説明します.
csv形式はカンマ区切りでデータが並んだテキストファイルです.先にテキストファイル(test.txt)として示したものは,元々csv形式のファイルだったので,以下のように数値の後にカンマが入っていました.
0.0,0.0,0.0014294350166200182,-0.001270499567236085,0.0006352497836180425,-0.001270499567236085
-0.0004764783388733394,0.0,0.0004764783388733394,0.0,0.001270499567236085,0.0
-0.0004764783388733394,-0.0004764783388733394,-0.0009529566777466788,0.0,0.0019057493508541274,0.001270499567236085
0.0,-0.0004764783388733394,0.0,-0.001270499567236085,0.0006352497836180425,-0.00254099913447217
-0.0009529566777466788,-0.0009529566777466788,-0.0014294350166200182,0.0,-0.001270499567236085,-0.0019057493508541274

以下の例では,まずcsvファイルとして,
a, b, c, d
1, 2, 3, 4
5, 6, 7, 8
9, 10, 11, 12
というcsvファイル(ここではsample.csv)があったとします.
このcsvファイルを読み込み,左から2列目の数字を足し算した結果を求めるプログラム例を以下に示します.

import csv                     # Import csv module
 
s = 0
with open("sample.csv") as f:
    reader = csv.reader(f)      # Read csv file
    next(reader)                # Advance one line
    for row in reader:
        s += int(row[1])        # Add numerical value of the second column from the left

print(s)
 
csvファイルの読み取りの仕組みは,まずテキストファイルを開き,そのファイルオブジェクトを引数にしてcsv.readerオブジェクトを作成することです.次に,next関数を用いることで,readerを1行分読み進めて,1行目を無視する処理をしています.
その後にfor文を使ってcsvファイルを1行分読み進め,読み取った内容はrowという変数に格納しています.2行目にあたるデータはrow[1]なので,その値を文字列から整数に変換して和を計算しています.
 
実行結果は以下のようになります.
18
 
続いて,csv形式のファイルに書き出す例を示します.
 
import csv                     # Import csv module
 
data = [[1, "a", 1.1],
        [2, "b", 1.2],
        [3, "c", 1.3]]
 
with open("output.csv", "w") as f:
    wr = csv.writer(f)
    for row in data:
        wr.writerow(row)
 
ここでは,リストが変数dataに格納されており,それをcsv形式としてファイルに書き込んでいます.csvwriterのインスタンスを作成してから,dataの要素を1つづつ読み込んでwriterowメソッドで書き込んでいます.これ以外の方法として,csvwriterクラスには,複数行を一気に書き込むwriterowsメソッドもあります.

上記のスクリプトを実行すると,output.csvという名前で,以下の内容が記載されたcsvファイルが作成されます.
1,a,1.1
2,b,1.2
3,c,1.3
 
json形式のファイルの読み書きにはjsonモジュールを用います.ファイルの出力は dump,文字列としての出力は dumps,ファイルからの入力は load,文字列からの入力は loads関数を使います.いかに,文字列への変換例を示します.
 
>>> import json
>>> data = {"a": 1, "b": "x", "c": [1, 2, 3], "d": {"a": 1, "b": 2}}
>>> s = json.dumps(data)
>>> s
'{"a": 1, "c": [1, 2, 3], "b": "x", "d": {"a": 1, "b": 2}}'
>>> data2 = json.loads(s)
>>> data2
{'a': 1, 'c': [1, 2, 3], 'b': 'x', 'd': {'a': 1, 'b': 2}}
このように,辞書とリストのネスト構造を持つデータはdumps関数を使うとjson形式の文字列に変換できます.また,json形式の文字列はloads関数で辞書とリスト構造のネストに変換されます.