csv モジュールメモ
ひさしぶりに csv モジュールを使ったのだけれど、うろおぼえで十数分無駄にしたので、簡単にメモしてみる。
csv モジュールとは
CSV のようなテキストからデータを読み出したり、書き出したりできる便利モジュール。
CSV って簡単そうに見えるけど、 'a,b,c'.split(',') とかやってしまうのはあまりに無防備。カンマ自身はどのように表現されているのか、など考えるべきことがいくつかある。こういった些細なつまづきをしないために、このモジュールがある。
なお、 csv という名前だけれども、 CSV の「ような」テキストが処理対象なのでタブ区切り、スペース区切りらのテキストにも対応可能。
読むときの小ネタ
CSV の 1 行目がデータでなく各列の説明、見出しになっていることはよくある。
id,name 0,fgshun 1,shun
これを事前に取り出しておくには next を使う。
>>> import csv >>> f = open(u'spam.csv', 'rb') >>> reader = csv.reader(f) >>> fieldnames = next(reader) >>> for data in reader: ... print data ... ['0', 'fgshun'] ['1', 'shun'] >>> print fieldnames ['id', 'name']
もしくは csv.DictReader を使う。本来、取り出したデータをインデックスアクセスではなく列名アクセスできるようにしてくれる(リストではなく辞書でデータをあつかう)ものだが、 1 行目のデータを列名として解釈してくれるという機能もある(列名を引数で与えなかったときの動作)。
>>> import csv >>> f = open(u'spam.csv', 'rb') >>> reader = csv.DictReader(f) >>> for data in reader: ... print data ... {'id': '0', 'name': 'fgshun'} {'id': '1', 'name': 'shun'} >>> reader.fieldnames ['id', 'name']
書くときの小ネタ
writer.writerows で複数行を一度に書き込むことができる。
for row in data: writer.writerow(row)
を
writer.writerows(data)
と書くことができるということ。
Windows で用いる際の注意点
ファイルはバイナリモードで開くこと。
細かい書式の差異への対応
csv モジュールはデフォルトでは csv.excel を書式パラメタとして用いる。これは Excel が出力するカンマ区切りファイルに対応するように作られている。
このほか、 csv.excel_tabl という Excel のタブ区切りファイルに対応するものもある。こちらを使うときは、 dialect 引数にこれのインスタンスをわたすようにする。
>>> f = open(u'spam.csv', 'rb') >>> reader = csv.reader(f, dialect=csv.excel_tab())
書式パラメタを自作するには csv.Dialect クラスを継承したクラスを作成すればよい。作成手順を知るにはこれらのコードを読むのが手っ取り早い。
class Dialect: """Describe an Excel dialect. This must be subclassed (see csv.excel). Valid attributes are: delimiter, quotechar, escapechar, doublequote, skipinitialspace, lineterminator, quoting. """ _name = "" _valid = False # placeholders delimiter = None quotechar = None escapechar = None doublequote = None skipinitialspace = None lineterminator = None quoting = None # 省略 class excel(Dialect): """Describe the usual properties of Excel-generated CSV files.""" delimiter = ',' quotechar = '"' doublequote = True skipinitialspace = False lineterminator = '\r\n' quoting = QUOTE_MINIMAL register_dialect("excel", excel) class excel_tab(excel): """Describe the usual properties of Excel-generated TAB-delimited files.""" delimiter = '\t' register_dialect("excel-tab", excel_tab)
各クラス属性の意味はこう。
- delimiter
- 列区切り文字
- quotechar
- カラム内容をクォートする際に用いられる文字。
- escapechar
- 列区切文字をエスケープする際に用いられる文字(None 可)
- doublequote
- quotechar 自身をどう処理するか。 True で二重化、 False でエスケープ(escapechar が None の時、実行時エラーとなる可能性あり)
- lineterminator
- 行区切り文字
- quoting
- クォート条件の指定
quoting に指定できる値は以下。
- csv.QUOTE_ALL
- 無条件ですべてをクォートする
- csv.QUOTE_MINIMAL
- 特殊な文字が含まれているときのみクォートする
- csv.QUOTE_NONNUMERIC
- 非数値のみクォートする(浮動小数点数が多用されたデータ向け)
- csv.QUOTE_NONE
- 特殊な文字をエスケープすることで処理する(escapechar が None の時、実行時エラーとなる可能性あり)
というわけで、たとえば「Excel の CSV っぽいのだけれども各カラムはすべてクォート」という書式はこのような感じで作成できる。
import csv class ExcelQuoteAll(csv.excel): quoting = csv.QUOTE_ALL
>>> from StringIO import StringIO >>> f = StringIO() >>> writer = csv.writer(f, ExcelQuoteAll()) >>> writer.writerows(( ... ('spam', 'ham', 'eggs'), ... ('spam,spam', '"ham"', 'eggs'))) >>> print f.getvalue() "spam","ham","eggs" "spam,spam","""ham""","eggs"