[Python3]ファイル(text, json, csv)の読み込み方の基本をまとめる

色々な方法があるが、基本的な手順は同じである。

1. ファイルをオープンする。
2. データを読み込む。

1番目は変わらない。2番目の読み込み方に色々な方法がある。

- ファイルの種類により使用する関数を変える
- ファイルの読み込み方を変える(ファイル全体、1行ずつ)

ファイルの読み込み方の概要をつかんでおいて、必要なものを深く調べるという学び方ができるようメモをまとめた。

# 環境
- Python 3.7.4

# フォルダ構成
```
data_read_test
├──data
│  ├──test.json
│  └──test.csv
├──data_read_test_1.py
├──data_read_test_2.py
├──data_read_test_3.py
├──json_read_test.py
└──csv_read_test.py
```

#ファイルオープンの方法
##組み込み関数open()
組み込み関数`open(path, mode)`を使う。
`path`には読み込み対象ファイルのパスを設定する
`mode`は、ファイルをオープンする時に目的に応じて設定しなければならない。読み込みの場合は"r"とする。"r"はデフォルト値なので省略可能だが、他に"w"書き込み、"a"追記などがある。

```python:
'''
ファイルオープンのサンプル
'''
path = 'data/test.json' #読み込み対象ファイル
mode = "r" #ファイルモード r:読み込み

f = open(path, mode) #ファイルをオープンする
pass #ここにデータを読み込む処理を記述する
f.close() #オープンしたファイルは、必ずクローズすること
```

##withブロックの使用
`with`ブロックを使うとブロック終了時にファイルが自動的にクローズされる。クローズ忘れのミスが起こりにくなるので便利。

```python:
'''
ファイルオープンのサンプル(withブロック)
'''
path = 'data/test.json' #読み込み対象ファイル
mode = "r" #ファイルモード r:読み込み

with open(path, mode) as f:
    pass #ここにデータを読み込む処理を記述する
    # f.close()は不要
    # withブロックを使うと、ファイルが自動的にクローズされる
```

#テキストデータを読み込む
##ファイル全体を文字列として読み込む
`open`したファイルオブジェクトに対して、`read`メソッドを使う。
サンプルコードでは、読み込み対象ファイルにJSONファイルを指定しているが、JSONファイルもテキストファイルなので、`read`メソッドにより、ファイル全体が文字列として読み込まれる。データタイプ(Type of "data")は`str`となる。
ただし、`read`メソッドは文字列として読むだけなので、JSON形式としては識別されない。

```python:data_read_test1.py
def data_read_test1(path, mode):
    """
    ファイル全体を文字列(str型)で読み込む
    """
    with open(path, "r") as f:
        data = f.read() #ファイル全体を読み込む
    return(data)

if __name__ == "__main__":
    #読み込み対象ファイル
    path = 'data/test.json'
    #ファイルモード
    mode = "r" #読み込み用

    data = data_read_test1(path, mode)

    print("Type of ""data"":{}".format(type(data)))
    print("---- All data ----")
    print(data)
```

読み込み対象ファイルの内容。

```test.json
{
 "book1":{
"title":"Evil Boy",
"author":"B.T."
},
"book2":{
"title":"Grappler",
"author":"H.B."
}
}
```

##行単位のリストとして読み込む
`read`メソッドは、ファイル全体を一塊の文字列データとして読み込む。1行ずつ処理したいときは、`readlines`メソッドを使う。`readlines`メソッドで読み込むと、データタイプ(Type of "data")が`list`となる。

```python:data_read_test2.py
def data_read_test2(path, mode):
    """
    1行ずつ処理できるよう行単位のリスト(list型)として読み込む
    """
    with open(path, "r") as f:
        data = f.readlines() #行単位のリストとして読み込む
    return(data)


if __name__ == "__main__":
    #読み込み対象ファイル
    path = 'data/test.json'
    #ファイルモード
    mode = "r" #読み込み用

    data = file_read_test2(path, mode)

    print("Type of ""data"":{}".format(type(data))) #strではなくlist
    print("---- All data ----")
    print(data)

    print("---- Each line ----")
    for line in data:
        print(line) #1行ずつ処理できる
```
##ファイルから1行ずつ読み込む
`read`メソッドや`readlines`メソッドは、ファイルをすべてメモリに読み込んでしまう。読み込み対象ファイルのサイズが巨大で、それが問題となる場合は、`readline`メソッド("s"なし)で1行ずつ読み込む。
`readline`メソッドは、1行だけ読み込むがある。もう一度読み込むと、次の行が読み込まれる。
このような1行ずつ読み込む概念を詳しく知りたい場合は、「イテレータ」や「next() メソッド」を調べてみるとよい。

```python:data_read_test3.py
def data_read_test3(path, mode):
    """
    ファイルから1行ずつ読み込む
    """
    with open(path, mode) as f:
        row = f.readline() #1行目を読み込む
        while row:
            print("---- Row data ----")
            print("Type of ""data"":{}".format(type(row))) #str型
            print(row)
            row = f.readline() #次の行を読み込む
 
if __name__ == "__main__":
    #読み込み対象ファイル
    path = 'data/test.json'
    #ファイルモード
    mode = "r" #読み込み用

    data_read_test3(path, mode)
```

#JSON形式のデータを読み込む
`open`したファイルオブジェクトに対して、jsonライブラリの`load`メソッドを使う。`load`メソッドを使うと、データをJSON形式として識別し、dict型に変換して読み込んでくれる。`load`メソッドで読み込むと、データタイプ(Type of "data")が`dict`となる。

```python:json_read_test.py
from json import load

def json_read_test(path, mode):
    """
    JSONファイル全体をdict型で読み込む
    """
    with open(path, mode) as f:
        data = load(f)
    
    return(data)


if __name__ == "__main__":
    #読み込み対象ファイル
    path = 'data/test.json'
    #ファイルモード
    mode = "r" #読み込み用

    data = json_read_test(path, mode)

    print("Type of ""data"":{}".format(type(data))) #strでもlistでもなくdict
    print("---- All data ----")
    print(data)
    print("---- Key data ----")
    for key in data:
        print(key) #json形式をdict型に変換しているので、例えば、keyのみを取り出せる
```

#csv形式のデータを読み込む

##ファイルから1行ずつ読み込む
`open`したファイルオブジェクトに対して、csvライブラリの`reader`メソッドを使う。`reader`メソッドを使うと、データをcsv形式として識別し、読み込んでくれる。
これまでと異なるのは、ファイルをクローズしてしまうと、dataの中から1行ずつ取り出せなくなるので、withブロックの中で処理していることである。

追記2020-01-01
このケースだけ、書き方が異なるのが悔しくて、同じような書き方になるよう検討しVer2を記述した。なお、withブロックの中にデータ処理を延々と書いていくというのは、読みやすいコードにならないと思う。

```python:csv_read_test.py
from csv import reader

def csv_read_test(path, mode):
    """
    csvファイル全体を読み込み行や要素を取り出す
    """
    with open(path, mode) as f:
        data = reader(f)

        for row in data:
            print("---- Row data ----")
            print("Type of ""data"":{}".format(type(row))) #list型
            print(row) #1行取り出せる
            print("---- Element data ----")
            print("Type of ""data"":{}".format(type(row[0]))) #str型
            print(row[0]) #要素を取り出せる


if __name__ == "__main__":
    #読み込み対象ファイル
    path = 'data/test.csv'
    #ファイルモード
    mode = "r" #読み込み用

    csv_read_test(path, mode)
```

読み込み対象ファイルの内容。

```test.csv
11,12,13,14
21,22,23,24
31,32,33,34
```

##ファイル全体を読み込む
`open`したファイルオブジェクトに対して、csvライブラリの`reader`メソッドを使う。`reader`メソッドを使うと、データをcsv形式として識別し、読み込んでくれる。ただし、`reader`メソッドの戻り値のタイプは、`<class '_csv.reader'>`というイテレータ(iterator)プロトコルに対応したオブジェクトなので、`for`文を使いすべての行をlistに格納して戻り値とする。
こうすると「JSON形式のデータを読み込む」と同じようにファイル全体を読み込んで戻り値とする書き方に統一できる。
なお、listへの格納では、リスト内包表記を使うとすっきりと書ける。

```python:csv_read_test.py
from csv import reader

def csv_read_test(path, mode):
    """
    csvファイル全体を読み込み、戻り値をlist型とする
    """
    with open(path, mode) as f:
        _reader = reader(f)
        data = [row for row in _reader] #リスト内包表記

        """ リスト内包表記を使わないとこんな感じ
        data = [] #空のlistを定義
        for row in _reader:
            data.append(row) #1行追加
        """
            
    return(data)

if __name__ == "__main__":
    #読み込み対象ファイル
    path = 'data/test.csv'
    #ファイルモード
    mode = "r" #読み込み用

    data = csv_read_test(path, mode)

    print("Type of ""data"":{}".format(type(data))) #list型
    print("---- All data ----")
    print(data)
        
    print("---- Row data ----")
    for row in data:
        print(row)

    print("Type of ""element"":{}".format(type(row[0]))) #str型
    print(row[0]) #要素を取り出せる
```

#参考
[Python: テキストファイルの読み込み – read()、readlines()、readline()メソッド](https://yukun.info/python-file/)
[【Python入門】JSON形式データの扱い方](https://qiita.com/Morio/items/7538a939cc441367070d)
[PythonでCSVファイルを読み込み・書き込み（入力・出力）](https://note.nkmk.me/python-csv-reader-writer/)

#更新 2020-01-01
「csv形式のデータを読み込む」のコードの書き方が、「JSON形式のデータを読み込む」と非統一なのが悔しくて、同じような書き方になるよう検討し、Ver2を追記した。

#更新 2020-01-04
ファイル全体を読み込む場合と、ファイルから1行ずつ読み込む場合の違いが分かるよう、「ファイルから1行ずつ読み込む」を追記した。その際、学習者の手がかりとして「イテレータ」という言葉を使った説明を加えた。