一日一万字の感謝の写経

一日一万字の感謝の写経をして強くなります。そのうち本を置き去りにして何かを作り始める予定です。

Pythonでのcsvファイルの編集方法をまとめる

目的

 最近、Pythoncsvを編集する機会がありました。そこで、そのときに得た知識をまとめておこうと思います。Pythoncsvファイルを操作するには、標準ライブラリのcsvモジュールとPandasを使用する方法があるようでしたが、今回は標準ライブラリのcsvモジュールを使用しました。参考にしたのは公式ドキュメントです。

14.1. csv --- CSV ファイルの読み書き — Python 3.5.10 ドキュメント

公式ドキュメントはかなり情報が多いですが、私のような初心者にはわかりにくい表現ばかりだったので、なるべく平易にまとめるように努力しました。

csvモジュールのインポート

 まず、csvモジュールをインポートしておきます。

import csv

csvファイルの読み込み方

ヘッダーの無い場合

 まずは、ヘッダーなどはなく、ただデータが並べられているcsvファイルをオープンする方法を紹介します。

# example.csv
佐藤,一郎,20
高橋,二郎,21

まず、csvファイルの最もシンプルな開き方は次のようにすることができます。

with open('example.csv', 'rt', newline='') as csvfile:  # まず、ファイルをオープンする
    reader = csv.reader(csvfile) # csvモジュールのreader関数でreaderという変数名のreaderオブジェクトを作成する
    profile = [row for row in reader] # リスト内包表記によりreaderの中身をリストに追加していく。for文でも可

上のコードを実行すると、profileという変数名のリストが作られます。

profile

Output:

[['佐藤', '一郎', '20'], ['高橋', '二郎', '21']]

 上記の説明をします。まず、一行目については、ファイルのオープンとクローズを自動的に行ってくれる構文と思ってください。openで'example.csv'という名前のファイルを開きます。'rt'は読み込み(read)かつテキストモード(text)で開くことを指定しています。newline=''を指定しておくことはおまじないとして公式ドキュメントで推奨されています。

newline='' が指定されない場合、クォートされたフィールド内の改行は適切に解釈されず、書き込み時に \r\n を行末に用いる処理系では余分な \r が追加されてしまいます。csv モジュールは独自 (universal newlines) の改行処理を行うため、newline='' を指定することは常に安全なはずです。

なるほどわからん

asの後ろで指定する変数名はオープンしたファイル(Pythonではファイルオブジェクトというオブジェクトだそう)を参照するのに使われます。

 次の行では、csvモジュールのreader関数を用いてcsvfileという変数名のオブジェクトからreaderという変数名のオブジェクト(readerオブジェクトというそう)を作成します。readerオブジェクトはイテレータとして使用することができ、つまりfor文によりループをすることができます。このとき1ループで扱われるオブジェクトは、csvファイル内の各行がカンマごとに文字列として分割されたリストになります。for文で実際に確認すると以下のようになります。

with open('example.csv', 'rt', newline='') as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        print(row)

Output:

['佐藤', '一郎', '20']
['高橋', '二郎', '21']

が1ループで扱われます。

 最後の

    profile = [row for row in reader]

によりreaderオブジェクトからリストを取り出し、example.csvというファイル内のデータが文字列のリストのリストとしてPythonで扱うことができる形になりました。

ヘッダーを指定して、辞書として読み込む場合

 次に、各列にヘッダーを指定して、Pythonの辞書としてデータを読み込む方法を説明します。それには、以下のようにします。

with open('example.csv', 'rt', newline='') as csvfile:
    reader = csv.DictReader(csvfile, fieldnames=['姓', '名', '年齢'])
    profile = [row for row in reader]

上記のコードを実行すると以下のような、OrderedDictを要素としたリストが得られます。

profile

Output:

    [OrderedDict([('姓', '佐藤'), ('名', '一郎'), ('年齢', '20')]),
     OrderedDict([('姓', '高橋'), ('名', '二郎'), ('年齢', '21')])]

csvモジュールのDictReaderfieldnamesに渡されたリストに指定されたヘッダーをキーとした辞書を返すイテレータを作成します。各キーのバリューにはexample.csvの各行の値が渡されます。

 また、以下のようにcsvファイルの一行目にヘッダーが書かれている場合、DictReaderfieldnamesを省略すると、自動的に一行目の値を辞書のキーとして処理をします。

# example2.csv
姓,名,年齢
佐藤,一郎,20
高橋,二郎,21
with open('example2.csv', 'rt', newline='') as csvfile:
    reader = csv.DictReader(csvfile)
    profile = [row for row in reader]
profile

Output:

    [OrderedDict([('姓', '佐藤'), ('名', '一郎'), ('年齢', '20')]),
     OrderedDict([('姓', '高橋'), ('名', '二郎'), ('年齢', '21')])]

csvファイルの書き込み方

 次に、csvファイルを作成して書き込む方法を説明します。

一行ずつ書き込む場合

 まず、一行ずつ書き込む方法を説明します。一行ずつ書き込むには以下のようにします。

with open('write_example.csv', 'wt', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['佐藤', '一郎', '20'])
    writer.writerow(['高橋', '二郎', '21'])
    writer.writerow(('渡辺', '三郎', '21'))

上記のコードを実行すると以下のようなwrite_example.csvファイルが作成されます。

# write_example.csv
佐藤,一郎,20
高橋,二郎,21
渡辺,三郎,21

 上記のコードの説明をします。まず、openの引数で'wt'を指定し、書き込み(write)かつテキストモードで開きます。

 次に、csvモジュールのwriter関数により、writerという変数名のオブジェクト(writerオブジェクトというそう)を作成します。このwriterオブジェクトはwriterowというメソッドを備えており、引数としてリスト(やタプル)を渡すと、それらの要素をカンマで区切り、write_example.csvへ書き込みます。

一度に複数行を書き込む場合

 以下のようにwriterowsメソッドを使用すると、リスト(やタプル)のリストを一行ずつ書き込むことができます。

profile = [['佐藤', '一郎', '20'], ['高橋', '二郎', '21'], ('渡辺', '三郎', '21')]
with open('write_example.csv', 'wt', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerows(profile)

ヘッダーを指定して書き込む場合

 ヘッダーを指定して書き込むには以下のようにDictWriterを使用します。

with open('write_example2.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({'姓': '佐藤', '名': '一郎', '年齢': '20'})
    writer.writerow({'姓': '高橋', '名': '二郎', '年齢': '21'})

上記のコードを実行すると以下のwrite_example2.csvが得られます。

# write_example2.csv
姓,名,年齢
佐藤,一郎,20
高橋,二郎,21

 上記のコードの説明をします。csvモジュールのDictWriterにファイルオブジェクトcsvfilefieldnames引数を渡します。fieldnamesにはcsvファイルのヘッダーを指定します。

 そして、writerowメソッドには、fieldnamesで指定したヘッダーをキーとする辞書を渡してwrite_example2.csvファイルへ記入していきます。

 ちなみに、writer.writeheader()はwrite_example2.csvファイルにヘッダーを書き込むコマンドなので、ヘッダーを書き込みたくない場合は省略できます。

ヘッダーを指定して一度に複数書き込む場合

 また、以下のようにwriterowsメソッドを使用すると辞書のリストを書き込むことができます。これも上記と同じwrite_example2.csvファイルを作ることができます。

profile = [{'姓': '佐藤', '名': '一郎', '年齢': '20'},
           {'姓': '高橋', '名': '二郎', '年齢': '21'}]
with open('write_example2.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(profile)

ヘッダーに指定したキーを全て持たない辞書を入力する際の処理

デフォルトでの動作

 以下のコードのように、fieldnames['姓', '名', '年齢', '結婚']を指定してみます。このとき、{'姓': '佐藤', '名': '一郎', '年齢': '20'}{'姓': '高橋', '名': '二郎', '年齢': '21'}は結婚というキーを持ちません。この場合は、結婚の列にあたる部分は空欄になります。write_example2.csvの佐藤さん、高橋さんの行の右端にコンマのみが記入されているので、空欄が記入されていることがわかります。

profile = [{'姓': '佐藤', '名': '一郎', '年齢': '20'},
           {'姓': '高橋', '名': '二郎', '年齢': '21'}]
with open('write_example2.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢', '結婚']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(profile)
    writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': '24', '結婚': '既婚'})
# write_example2.csv
姓,名,年齢,結婚
佐藤,一郎,20,
高橋,二郎,21,
渡辺,三郎,24,既婚
restvalによる足りないキーの処理

 上のコードでは、存在しないキーの箇所には空欄が記入されましたが、DictWriterrestvalを指定すると、指定した文字列を補います。以下のコードでは、restval='未記入'を指定して、結婚というキーを持たない辞書を書き込む際に未記入という文字列を補うようにしてあります。

with open('write_example2.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢', '結婚']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames, restval='未記入')
    writer.writeheader()
    writer.writerows(profile)
    writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': '24', '結婚': '既婚'})
姓,名,年齢,結婚
佐藤,一郎,20,未記入
高橋,二郎,21,未記入
渡辺,三郎,24,既婚

ヘッダーに指定ていないキーを持つ辞書を入力する際の処理

デフォルトでの動作

 以下のコードのように、fieldnames = ['姓', '名', '年齢', '結婚']のようにヘッダーを指定しているときに、コメントというキーを持つ辞書を書き込むとエラーになります。

profile = [{'姓': '佐藤', '名': '一郎', '年齢': '20', '結婚': '未記入'}, 
           {'姓': '高橋', '名': '二郎', '年齢': '21', '結婚': '未記入'}]
with open('write_example2.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢', '結婚']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(profile)
    writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': '24', '結婚': '既婚'})
    writer.writerow({'姓': '鈴木', '名': '四郎', '年齢': '25', '結婚': '未婚', 'コメント': '彼女ほしい'})
    ---------------------------------------------------------------------------

    ValueError                                Traceback (most recent call last)

    <ipython-input-83-251ee449288c> in <module>
          5     writer.writerows(profile)
          6     writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': '24', '結婚': '既婚'})
    ----> 7     writer.writerow({'姓': '鈴木', '名': '四郎', '年齢': '25', '結婚': '未婚', 'コメント': '彼女ほしい'})
    

    ~\Anaconda3\lib\csv.py in writerow(self, rowdict)
        153 
        154     def writerow(self, rowdict):
    --> 155         return self.writer.writerow(self._dict_to_list(rowdict))
        156 
        157     def writerows(self, rowdicts):
    

    ~\Anaconda3\lib\csv.py in _dict_to_list(self, rowdict)
        149             if wrong_fields:
        150                 raise ValueError("dict contains fields not in fieldnames: "
    --> 151                                  + ", ".join([repr(x) for x in wrong_fields]))
        152         return (rowdict.get(key, self.restval) for key in self.fieldnames)
        153 
    

    ValueError: dict contains fields not in fieldnames: 'コメント'
extrasactionによるヘッダーに存在しないキーの処理

 上のコードのように、ヘッダーに指定していないキーを持つ辞書を書き込もうとするとエラーが出ます。しかし、DictWriterextrasaction'ignore'を指定すると、エラーを無視してcsvファイルに書き込みを行います。

with open('write_example2.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢', '結婚']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames, extrasaction='ignore')
    writer.writeheader()
    writer.writerows(profile)
    writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': '24', '結婚': '既婚'})
    writer.writerow({'姓': '鈴木', '名': '四郎', '年齢': '25', '結婚': '未婚', 'コメント': '彼女ほしい'})
# write_example2.csv
姓,名,年齢,結婚
佐藤,一郎,20,未記入
高橋,二郎,21,未記入
渡辺,三郎,24,既婚
鈴木,四郎,25,未婚

その他のオプションについて

コンマ以外の区切り文字

 writer関数のdelimiterをしていすると、区切り文字をコンマから変更することができます。例えば、以下のようにdelimiter=' 'を指定すると、出力されるcsvファイルはスペース区切りになります。

profile = [['佐藤', '一郎', '20'], ['高橋', '二郎', '21'], ('渡辺', '三郎', '21')]
with open('write_example3.csv', 'wt', newline='') as csvfile:
    writer = csv.writer(csvfile, delimiter=' ')
    writer.writerows(profile)
#write_example3.csv
佐藤 一郎 20
高橋 二郎 21
渡辺 三郎 21

また、読み込む場合でもdelimiterを指定することができます。以下のように、delimiterを指定するのとしないのでは、読み込まれるリストの中身が変わってきます。以下は、上で作成したwrite_example3.csvファイルを読み込んだ時の例です。

# delimiter指定なし
with open('write_example3.csv', 'rt', newline='') as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        print(row)

Output:

# 一行が一つの文字列として読み込まれる
['佐藤 一郎 20']
['高橋 二郎 21']
['渡辺 三郎 21']
# delimiter指定あり
with open('write_example3.csv', 'rt', newline='') as csvfile:
    reader = csv.reader(csvfile, delimiter=' ')
    for row in reader:
        print(row)

Output:

# スペースごとに区切られた文字列のリストとして読み込まれる。
['佐藤', '一郎', '20']
['高橋', '二郎', '21']
['渡辺', '三郎', '21']

書き込み時のクオーティング

 書き込む文字列にコンマ等の文字列が含まれているが、それを一つの要素として扱いたい場合がある。その時はwriterDictWriterquotingを指定する。

全部をクオーティングする場合

quoting=csv.QUOTE_ALLを指定すると、書き込み時にすべての項目をクオーティングする。

with open('write_example4.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢', '結婚', 'コメント']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames, quoting=csv.QUOTE_ALL)
    writer.writeheader()
    writer.writerow({'姓': '佐藤', '名': '一郎', '年齢': '20', '結婚': '未婚', 'コメント': 'お金欲しい'})
    writer.writerow({'姓': '高橋', '名': '二郎', '年齢': '21', '結婚': '未婚', 'コメント': '時間が,欲しい'})
    writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': '24', '結婚': '既婚', 'コメント': 'お腹すいた'})
    writer.writerow({'姓': '鈴木', '名': '四郎', '年齢': '25', '結婚': '未婚', 'コメント': '彼女ほしい'})
# write_example4
"姓","名","年齢","結婚","コメント"
"佐藤","一郎","20","未婚","お金欲しい"
"高橋","二郎","21","未婚","時間が,欲しい"
"渡辺","三郎","24","既婚","お腹すいた"
"鈴木","四郎","25","未婚","彼女ほしい"

 また、クオーティングの記号もquotecharにより指定することができます。以下では、quotechar="'"を指定して、シングルクオートでクオーティングするようにしてあります。

with open('write_example5.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢', '結婚', 'コメント']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames, quoting=csv.QUOTE_ALL, quotechar="'")
    writer.writeheader()
    writer.writerow({'姓': '佐藤', '名': '一郎', '年齢': '20', '結婚': '未婚', 'コメント': 'お金欲しい'})
    writer.writerow({'姓': '高橋', '名': '二郎', '年齢': '21', '結婚': '未婚', 'コメント': '時間が,欲しい'})
    writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': '24', '結婚': '既婚', 'コメント': 'お腹すいた'})
    writer.writerow({'姓': '鈴木', '名': '四郎', '年齢': '25', '結婚': '未婚', 'コメント': '彼女ほしい'})
'姓','名','年齢','結婚','コメント'
'佐藤','一郎','20','未婚','お金欲しい'
'高橋','二郎','21','未婚','時間が,欲しい'
'渡辺','三郎','24','既婚','お腹すいた'
'鈴木','四郎','25','未婚','彼女ほしい'
delimiterquotecharまたはlineterminatorを含む文字列のみをクオーティングする場合

区切り文字(delimiter)、クオート文字(quotechar)または改行文字(lineterminator)を含む文字列のみをクオートする場合はquoting=csv.QUOTE_MINIMALを指定します。以下の例では、"時間が,欲しい"という文字列にdelimiterである","が含まれているので、write_example6.csvには"時間が,欲しい"というようにクオーティングされた文字列が出力されています。同様に、 '"彼女"ほしい'という文字列にはquotecharである'"'が含まれているので、write_example6.csvには"""彼女""ほしい"というようにクオーティングされた文字列が出力されています。ただし、'"彼女"ほしい'のダブルクオートが二つ重ねて出力されるようです。

with open('write_example6.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢', '結婚', 'コメント']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames, quoting=csv.QUOTE_MINIMAL)
    writer.writeheader()
    writer.writerow({'姓': '佐藤', '名': '一郎', '年齢': '20', '結婚': '未婚', 'コメント': 'お金欲しい'})
    writer.writerow({'姓': '高橋', '名': '二郎', '年齢': '21', '結婚': '未婚', 'コメント': '時間が,欲しい'})
    writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': '24', '結婚': '既婚', 'コメント': 'お腹すいた'})
    writer.writerow({'姓': '鈴木', '名': '四郎', '年齢': '25', '結婚': '未婚', 'コメント': '"彼女"ほしい'})
# write_example6.csv
姓,名,年齢,結婚,コメント
佐藤,一郎,20,未婚,お金欲しい
高橋,二郎,21,未婚,"時間が,欲しい"
渡辺,三郎,24,既婚,お腹すいた
鈴木,四郎,25,未婚,"""彼女""ほしい"
非数値のみをクオーティングする場合

 以下のように、辞書のバリューに整数と文字列が含まれる場合に、文字列のみをクオーティングするにはquoting=csv.QUOTE_NONNUMERICを指定します。

with open('write_example7.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢', '結婚', 'コメント']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames, quoting=csv.QUOTE_NONNUMERIC)
    writer.writeheader()
    writer.writerow({'姓': '佐藤', '名': '一郎', '年齢': 20, '結婚': '未婚', 'コメント': 'お金欲しい'})
    writer.writerow({'姓': '高橋', '名': '二郎', '年齢': 21, '結婚': '未婚', 'コメント': '時間が欲しい'})
    writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': 24, '結婚': '既婚', 'コメント': 'お腹すいた'})
    writer.writerow({'姓': '鈴木', '名': '四郎', '年齢': 25, '結婚': '未婚', 'コメント': '彼女ほしい'})
# write_example7.csv
"姓","名","年齢","結婚","コメント"
"佐藤","一郎",20,"未婚","お金欲しい"
"高橋","二郎",21,"未婚","時間が欲しい"
"渡辺","三郎",24,"既婚","お腹すいた"
"鈴木","四郎",25,"未婚","彼女ほしい"
クオーティングはせず、エスケープ文字により処理する場合

 クオーティングではなく、エスケープ文字を指定することによりdelimiterquotecharを処理する場合は以下のようにします。まず、quoting=csv.QUOTE_NONEを指定します。これは、書き込み時にクオーティングは一切せず、delimiterquotecharが文字列に現れた場合はescapecharに指定した文字列でエスケープするようなオプションです。(ドキュメントにはdelimiterのみをエスケープすると書かれていましたが、quotecharエスケープするようです。)今回はescapechar='\\'を指定しました。

with open('write_example8.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢', '結婚', 'コメント']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames, quoting=csv.QUOTE_NONE, escapechar='\\')
    writer.writeheader()
    writer.writerow({'姓': '佐藤', '名': '一郎', '年齢': '20', '結婚': '未婚', 'コメント': 'お金欲しい'})
    writer.writerow({'姓': '高橋', '名': '二郎', '年齢': '21', '結婚': '未婚', 'コメント': '時間が,欲しい'})
    writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': '24', '結婚': '既婚', 'コメント': 'お腹すいた'})
    writer.writerow({'姓': '鈴木', '名': '四郎', '年齢': '25', '結婚': '未婚', 'コメント': '"彼女"ほしい'})
# write_example8.csv
姓,名,年齢,結婚,コメント
佐藤,一郎,20,未婚,お金欲しい
高橋,二郎,21,未婚,時間が\,欲しい
渡辺,三郎,24,既婚,お腹すいた
鈴木,四郎,25,未婚,\"彼女\"ほしい

 ちなみに、escapecharを指定しないとエラーになります。

with open('write_example8.csv', 'wt', newline='') as csvfile:
    fieldnames = ['姓', '名', '年齢', '結婚', 'コメント']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames, quoting=csv.QUOTE_NONE)
    writer.writeheader()
    writer.writerow({'姓': '佐藤', '名': '一郎', '年齢': '20', '結婚': '未婚', 'コメント': 'お金欲しい'})
    writer.writerow({'姓': '高橋', '名': '二郎', '年齢': '21', '結婚': '未婚', 'コメント': '時間が,欲しい'})
    writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': '24', '結婚': '既婚', 'コメント': 'お腹すいた'})
    writer.writerow({'姓': '鈴木', '名': '四郎', '年齢': '25', '結婚': '未婚', 'コメント': '彼女ほしい'})
    ---------------------------------------------------------------------------

    Error                                     Traceback (most recent call last)

    <ipython-input-103-dbe28ebf55ec> in <module>
          4     writer.writeheader()
          5     writer.writerow({'姓': '佐藤', '名': '一郎', '年齢': '20', '結婚': '未婚', 'コメント': 'お金欲しい'})
    ----> 6     writer.writerow({'姓': '高橋', '名': '二郎', '年齢': '21', '結婚': '未婚', 'コメント': '時間が,欲しい'})
          7     writer.writerow({'姓': '渡辺', '名': '三郎', '年齢': '24', '結婚': '既婚', 'コメント': 'お腹すいた'})
          8     writer.writerow({'姓': '鈴木', '名': '四郎', '年齢': '25', '結婚': '未婚', 'コメント': '彼女ほしい'})
    

    ~\Anaconda3\lib\csv.py in writerow(self, rowdict)
        153 
        154     def writerow(self, rowdict):
    --> 155         return self.writer.writerow(self._dict_to_list(rowdict))
        156 
        157     def writerows(self, rowdicts):
    

    Error: need to escape, but no escapechar set

読み込み時のクオーティングの処理

クオーティングされていないフィールドをフロート型として読み込む

 以下のexample3.csvのように、数字以外がクオーティングされているcsvファイルを読み込む際に、数字のフィールドをPythonのフロート型として読み込むことができます。

# example3.csv
"姓","名","年齢","結婚","コメント"
"佐藤","一郎",20,"未婚","お金欲しい"
"高橋","二郎",21,"未婚","時間が欲しい"
"渡辺","三郎",24,"既婚","お腹すいた"
"鈴木","四郎",25,"未婚","彼女ほしい"

そのためには、quoting=csv.QUOTE_NONNUMERICを指定します。

with open('example3.csv', 'rt', newline='') as csvfile:
    reader = csv.DictReader(csvfile, quoting=csv.QUOTE_NONNUMERIC)
    profile = [row for row in reader]
profile

Output:

    [OrderedDict([('姓', '佐藤'),
                  ('名', '一郎'),
                  ('年齢', 20.0),
                  ('結婚', '未婚'),
                  ('コメント', 'お金欲しい')]),
     OrderedDict([('姓', '高橋'),
                  ('名', '二郎'),
                  ('年齢', 21.0),
                  ('結婚', '未婚'),
                  ('コメント', '時間が欲しい')]),
     OrderedDict([('姓', '渡辺'),
                  ('名', '三郎'),
                  ('年齢', 24.0),
                  ('結婚', '既婚'),
                  ('コメント', 'お腹すいた')]),
     OrderedDict([('姓', '鈴木'),
                  ('名', '四郎'),
                  ('年齢', 25.0),
                  ('結婚', '未婚'),
                  ('コメント', '彼女ほしい')])]
クオート文字をそのまま読み込む場合

example3.csvのように、クオートされているフィールドが含まれるファイルを読み込もうとすると

# example3.csv
"姓","名","年齢","結婚","コメント"
"佐藤","一郎",20,"未婚","お金欲しい"
"高橋","二郎",21,"未婚","時間が欲しい"
"渡辺","三郎",24,"既婚","お腹すいた"
"鈴木","四郎",25,"未婚","彼女ほしい"

以下のように自動的に変換されて読み込まれます。

with open('example3.csv', 'rt', newline='') as csvfile:
    reader = csv.DictReader(csvfile)
    profile = [row for row in reader]
profile

Output:

[OrderedDict([('姓', '佐藤'),
              ('名', '一郎'),
              ('年齢', '20'),
              ('結婚', '未婚'),
              ('コメント', 'お金欲しい')]),
 OrderedDict([('姓', '高橋'),
              ('名', '二郎'),
              ('年齢', '21'),
              ('結婚', '未婚'),
              ('コメント', '時間が欲しい')]),
 OrderedDict([('姓', '渡辺'),
              ('名', '三郎'),
              ('年齢', '24'),
              ('結婚', '既婚'),
              ('コメント', 'お腹すいた')]),
 OrderedDict([('姓', '鈴木'),
              ('名', '四郎'),
              ('年齢', '25'),
              ('結婚', '未婚'),
              ('コメント', '彼女ほしい')])]

これをクオート文字を含めて読み込みたい場合はquoting=csv.QUOTE_NONEを指定して読み込みます。

with open('example3.csv', 'rt', newline='') as csvfile:
    reader = csv.DictReader(csvfile, quoting=csv.QUOTE_NONE)
    profile = [row for row in reader]
profile

Output:

    [OrderedDict([('"姓"', '"佐藤"'),
                  ('"名"', '"一郎"'),
                  ('"年齢"', '20'),
                  ('"結婚"', '"未婚"'),
                  ('"コメント"', '"お金欲しい"')]),
     OrderedDict([('"姓"', '"高橋"'),
                  ('"名"', '"二郎"'),
                  ('"年齢"', '21'),
                  ('"結婚"', '"未婚"'),
                  ('"コメント"', '"時間が欲しい"')]),
     OrderedDict([('"姓"', '"渡辺"'),
                  ('"名"', '"三郎"'),
                  ('"年齢"', '24'),
                  ('"結婚"', '"既婚"'),
                  ('"コメント"', '"お腹すいた"')]),
     OrderedDict([('"姓"', '"鈴木"'),
                  ('"名"', '"四郎"'),
                  ('"年齢"', '25'),
                  ('"結婚"', '"未婚"'),
                  ('"コメント"', '"彼女ほしい"')])]

Dialectについて

 ダイアレクトって公式ドキュメントを始め読んでいた時は何を言っているかわからなかったのですが、要するに、「delimiter, escapechar, quotecharquotingなどの設定がまとまっていて、writerreader関数の引数に指定することでそれらの設定を適用させることができる」ようなものらしい。  実際に、writerreaderdialect引数としてデフォルトでは'excel'というダイアレクトが指定されている。

help(csv.writer)

Output:

Help on built-in function writer in module _csv:

writer(...)
    csv_writer = csv.writer(fileobj [, dialect='excel']
                                [optional keyword args])
        for row in sequence:
            csv_writer.writerow(row)
    
        [or]
    
        csv_writer = csv.writer(fileobj [, dialect='excel']
                                [optional keyword args])
        csv_writer.writerows(rows)
    
    The "fileobj" argument can be any object that supports the file API.

 そこで、'excel'というダイアレクトにはどのような設定がされているのかを実際に見てみると以下のようになっているのがわかります。

a = csv.excel # excelというダイアレクトのクラスのオブジェクトを作成する

delimiterなどの設定はアトリビュートとして設定されています。

a.delimiter

Output:

','
print(a.escapechar)

Output:

None
a.quotechar

Output:

'"'
a.quoting

Output:

0

なるほど。実際に設定されているようです。  'excel'以外のダイアレクトとして'excel-tab', 'unix_dialect'があり、自分が扱いたいファイルに応じて選択すればよさそうですね。

終わり

 以上で、csvモジュールのまとめを終わりにします。ブログを書き始めて経験が浅いので、表記ゆれや誤字もあると思いますし、そもそもPythonやプログラミングの知識も浅いので、言葉の使い方も正確ではないかもしれませんが、少しでも皆様の役に立てれば幸いです。

入門 Python 3

入門 Python 3

  • 作者:Bill Lubanovic
  • 発売日: 2015/12/01
  • メディア: 単行本(ソフトカバー)
みんなのPython 第4版

みんなのPython 第4版

  • 作者:柴田 淳
  • 発売日: 2016/12/22
  • メディア: 単行本