Pythonの便利技と注意点:ファイル読み書き編

[目次に戻る](https://qiita.com/yuta-sanjyo/items/416b05f8498df1ed3f49)

# 辞書形式でCSVファイルに出力する
```Py:DictWriter()関数を使用する
# csvパッケージをインポート
import csv

# 列名設定
fieldnames = ['Menu', 'Price']

# ファイル書き込み開始
# ※１行ずつ空いてしまう場合、newline=''オプションをつける
with open('example.csv', 'w', newline='') as f:

    # csvパッケージのDictWriter()関数を呼び出し、ファイル名と列名を指定
    writer = csv.DictWriter(f, fieldnames)

    # 最初に列を書く
    writer.writeheader()

    # writerow()関数を使って、辞書形式で{'列名': '値'}を書き込む
    writer.writerow({'Menu': 'ハンバーガー', 'Price': 300})
    writer.writerow({'Menu': 'ポテト', 'Price': 150})
    writer.writerow({'Menu': 'ドリンク', 'Price': 100})
```

```:出力結果（example.csv）
Menu,Price
ハンバーガー,300
ポテト,150
ドリンク,100

```

# CSVファイルを辞書形式で1行ずつ読み込む
```Py:DictReader()関数を使用する
# csvパッケージをimport
import csv

# CSVファイルを読みとりで開く
# ※１行ずつ空いてしまう場合、newline=''オプションをつける
with open('example.csv', 'r', newline='') as f:

    # csvパッケージのDictReader()関数を呼び出し、ファイル名を指定
    reader = csv.DictReader(f)

    # for文で辞書型で読み込んだファイルを1行ずつ変数に格納して
    # 変数['列名']と指定する
    for row in reader:
        print(row['Menu'], row['Price'])
```

```:出力結果（example.csv）
ハンバーガー 300
ポテト 150
ドリンク 100
```

# CSVファイルを読み込んでデータを更新する技
csv.DictWriterで読み込んだものをアップデートする方法が見つからなかったため、下記手順を行った。

1．辞書型変数準備
2．CSVfileをcsv.DictReaderで1行ずつ読み込み、1．で準備した辞書型変数に追加
3．辞書型変数の中の値を更新
4．更新し終えたら`with open('CSVファイル名', 'w', newline='')`でCSVファイルを上書きモードで開く
5．csv.DictWriter()関数を使ってCSVファイルを丸ごと上書きする

```:更新前のCSVファイル（example.csv）
Menu,Price
ハンバーガー,300
ポテト,150
ドリンク,100

```

```Py:例としてポテトを200円に更新する
# csvパッケージをimport
import csv

# 辞書型変数を準備
menu_dic = {}

# 列名設定
fieldnames = ['Menu', 'Price']

# CSVファイルを1行ずつ読み込んで、準備した辞書型変数に格納
with open('example.csv', 'r', newline='') as f:
    reader = csv.DictReader(f)
    for row in reader:
        menu_dic.update({row['Menu']: int(row['Price'])})

# 確認のため以下のコードを実行すると、下記のように出力される
# 
# 更新前辞書型変数
# {'ハンバーガー': 300, 'ポテト': 150, 'ドリンク': 100}

print('更新前辞書型変数')
print(menu_dic)

# 準備した辞書型変数内の「ポテト」の値段を更新
menu_dic['ポテト'] = int(200)

# 確認のため以下のコードを実行すると、下記のように出力される
# 
# 更新前辞書型変数
# {'ハンバーガー': 300, 'ポテト': 200, 'ドリンク': 100}
print('更新後辞書型変数')
print(menu_dic)

# CSVファイルを上書きモードで開く
with open('example.csv', 'w', newline='') as f:

    # csv.DictWriter()関数で列名までCSVファイルに出力
    writer = csv.DictWriter(f, fieldnames)
    writer.writeheader()

    # for文とitems()関数を使い、writerow関数を使って
    # 辞書型変数の中身を、辞書形式（{'列名': 値}）で
    # CSVファイルに出力する
    for menu, price in menu_dic.items():
        writer.writerow({'Menu': menu, 'Price': price})
```

```:更新後のCSVファイル（example.csv）
Menu,Price
ハンバーガー,300
ポテト,200
ドリンク,100

```

# CSVファイルの内容をデータの並び順ではない順番で表示後、データを追加・更新した上でCSVファイルに反映させる技

例として、値段の高いメニューから順番に表示して、メニューを追加・更新したものをCSVファイルに反映させるとする。

CSVファイルが下記のようになっている場合、一番高いダブルバーガーから表示して、最後はハンバーガを表示する。
その後、ポテト200円を追加して、ダブルバーガーを450円に変更するとする。

```:更新前CSVファイル（example.csv）
Menu,Price
ハンバーガー,300
チーズバーガー,400
ダブルバーガー,500
てりやきバーガー,350
```

手順は下記の通り。
1．CSVファイルを読み込んでOriginal_Menu_Dicに格納
2．Original_Menu_Dicをコピーして表示用の辞書Display_Menu_Dicに複製
3．一番値段の高いデータを表示して、そのデータをDisplay_Menu_Dicから削除、を繰り返す
4．全て表示してDisplay_Menu_Dicのデータがなくなったら、Original_Menu_Dicにポテト200円を追加して、ダブルバーガー450円を更新する
5．更新されたOriginal_Menu_DicをCSVファイルに上書き保存

```Py:
# deepcopy関数とcsvパッケージをimport
from copy import deepcopy
import csv


# 辞書型変数宣言
Original_Menu_Dic = {}
Display_Menu_Dic = {}

# 列名設定
fieldnames = ['Menu', 'Price']

# Original_Menu_Dic作成
with open('example.csv', 'r', newline='') as f:
    reader = csv.DictReader(f)
    for row in reader:
        Original_Menu_Dic.update({row['Menu']: int(row['Price'])})

# Display_Menu_Dic作成
Display_Menu_Dic = deepcopy(Original_Menu_Dic)

# 確認のため以下のコードを実行すると、下記のように出力される
#
# 更新前辞書型変数
# Original_Menu_Dic: {'ハンバーガー': 300, 'チーズバーガー': 400, 'ダブルバーガー': 500, 'てりやきバーガー': 350}
# Display_Menu_Dic: {'ハンバーガー': 300, 'チーズバーガー': 400, 'ダブルバーガー': 500, 'てりやきバーガー': 350}
print('更新前辞書型変数')
print('Original_Menu_Dic:', Original_Menu_Dic)
print('Display_Menu_Dic:', Display_Menu_Dic)
print('')

# メニューの中身を値段の高い順に表示
while len(Display_Menu_Dic) > 0:

    # 使用する変数を初期化
    # ※ループの度に初期化しないと前のループの段階で格納された値が残る
    tmp_menu = ''
    tmp_price = 0
    max_menu = ''
    max_price = 0

    # for文とitems()関数でメニューの中で一番高額なメニューを探しだす
    for tmp_menu, tmp_price in Display_Menu_Dic.items():

        # 一番高いメニューに何も入ってない（ループの一番最初）
        # あるいは
        # 読み込んだメニューの方が高額であれば
        # そのメニューを一番高額なメニューとして設定する
        if (max_menu == '') | (tmp_price > max_price):
            max_menu = tmp_menu
            max_price = tmp_price

    # 一番高額なメニューを表示して
    # 同じメニューを表示しないようにDisplay_Menu_Dicから削除する
    print('メニュー：', max_menu, '価格：', max_price)
    del Display_Menu_Dic[max_menu]

# 確認のため以下のコードを実行すると、下記のように出力される
# 表示用の辞書からはデータが全て消えているが
# 辞書データをdeepcopy()したため、元のメニューには影響がない
#
# メニュー表示後辞書型変数
# Original_Menu_Dic: {'ハンバーガー': 300, 'チーズバーガー': 400, 'ダブルバーガー': 500, 'てりやきバーガー': 350}
# Display_Menu_Dic: {}
print('')
print('メニュー表示後辞書型変数')
print('Original_Menu_Dic:', Original_Menu_Dic)
print('Display_Menu_Dic:', Display_Menu_Dic)
print('')

# ポテト200円を追加、ダブルバーガーの値段を450円に更新
Original_Menu_Dic['ポテト'] = int(200)
Original_Menu_Dic['ダブルバーガー'] = int(450)

# 確認のため以下のコードを実行すると、下記のように出力される
#
# メニュー更新後辞書型変数
# Original_Menu_Dic: {'ハンバーガー': 300, 'チーズバーガー': 400, 'ダブルバーガー': 450, 'てりやきバーガー': 350, 'ポテト': 200}
# Display_Menu_Dic: {}
print('メニュー更新後辞書型変数')
print('Original_Menu_Dic:', Original_Menu_Dic)
print('Display_Menu_Dic:', Display_Menu_Dic)
print('')

# 更新を終えたOriginal_Menu_Dicを
# csv.DictWriter()関数を使ってCSVファイルに上書きする
with open('example.csv', 'w', newline='') as f:
    writer = csv.DictWriter(f, fieldnames)
    writer.writeheader()

    for menu, price in Original_Menu_Dic.items():
        writer.writerow({'Menu': menu, 'Price': price})
```

```:出力結果
メニュー： ダブルバーガー 価格： 500
メニュー： チーズバーガー 価格： 400
メニュー： てりやきバーガー 価格： 350
メニュー： ハンバーガー 価格： 300
```

```:更新後CSVファイル（example.csv）
Menu,Price
ハンバーガー,300
チーズバーガー,400
ダブルバーガー,450
てりやきバーガー,350
ポテト,200

```

# ファイルに書き込んだ内容を書き込みの直後に確認する
```Py:'w'オプションだけではエラーが出力される
menu = '''\
カレー：500円
たこ焼き：300円
フランクフルト：150円
フライドポテト：200円
焼きトウモロコシ：300円\
'''

with open('menu.txt', 'w') as f:
    f.write(menu)
    print(f.read())
================================
# 出力結果
Traceback (most recent call last):
  File "<パス省略>", line 11, in <module>
    print(f.read())
          ^^^^^^^^
io.UnsupportedOperation: not readable
```

```Py:'w+'オプションでも何も表示されない
menu = '''\
カレー：500円
たこ焼き：300円
フランクフルト：150円
フライドポテト：200円
焼きトウモロコシ：300円\
'''

with open('menu.txt', 'w+') as f:
    f.write(menu)
    print(f.read())
================================
# 出力結果
# 全てを書き込んだ直後は、該当ファイルの一番最後の位置に来ているため
```

```Py:'w+'オプションに追加してf.seek(0)で書き込んだファイルの最初の位置に戻す必要がある
menu = '''\
カレー：500円
たこ焼き：300円
フランクフルト：150円
フライドポテト：200円
焼きトウモロコシ：300円\
'''

with open('menu.txt', 'w+') as f:
    f.write(menu)
    f.seek(0)
    print(f.read())
=================================
# 出力結果
カレー：500円
たこ焼き：300円
フランクフルト：150円
フライドポテト：200円
焼きトウモロコシ：300円
```

# CSVファイル読み込み時に行ごとに改行が入るのを防ぐ
```Py:Windowsでcsv.DictWriter()関数でCSV出力すると1行ごとにあいてしまう
import csv

fieldnames = ['Menu', 'Price']

with open('example.csv', 'w') as f:
    writer = csv.DictWriter(f, fieldnames)
    writer.writeheader()
    writer.writerow({'Menu': 'ハンバーガー', 'Price': 300})
    writer.writerow({'Menu': 'ポテト', 'Price': 150})
    writer.writerow({'Menu': 'ドリンク', 'Price': 100})
```

```:出力結果（example.csv）
Menu,Price

ハンバーガー,300

ポテト,150

ドリンク,100

```

```Py:ファイルを開く時にnewline=''オプションを追加することで１行空きを防ぐことができる
import csv

fieldnames = ['Menu', 'Price']

with open('example.csv', 'w', newline='') as f:
    writer = csv.DictWriter(f, fieldnames)
    writer.writeheader()
    writer.writerow({'Menu': 'ハンバーガー', 'Price': 300})
    writer.writerow({'Menu': 'ポテト', 'Price': 150})
    writer.writerow({'Menu': 'ドリンク', 'Price': 100})
```

```:出力結果（example.csv）
Menu,Price
ハンバーガー,300
ポテト,150
ドリンク,100

```

# テキストファイルに指定した文字列を埋め込んで表示する
```:変数を入れたテキストファイルを準備（message.txt）
本日は $weather なり。
```

```Py:stringパッケージをimportしてTemplate()関数とsubstitute()関数を使う
import string

# Template()関数でテキストファイルを変数に格納
with open('message.txt') as f:
    msg = string.Template(f.read())

# substitute()関数を使用して、テキストファイル内の$weather変数に文字列を渡す
print(msg.substitute(weather='晴天'))
=====================================
# 実行結果
本日は 晴天 なり。
```

[目次に戻る](https://qiita.com/yuta-sanjyo/items/416b05f8498df1ed3f49)