13.1. "csv" --- CSV ファイルの読み書き
**************************************

バージョン 2.3 で追加.

CSV (Comma Separated Values、カンマ区切り値列) と呼ばれる形式は、 スプ
レッドシートやデータベース間でのデータのインポートやエクスポートにおけ
る最も一般的な形式です。"CSV 標準" は存在しないため、 CSV 形式はデータ
を読み書きする多くのアプリケーション上の操作に応じて定義されているにす
ぎません。標準がないということは、異なるアプリケーション によって生成
されたり取り込まれたりするデータ間では、しばしば微妙な違いが発生すると
いうことを意味します。こうした違いのために、複数のデータ源から得られた
CSV ファイルを処理する作業が鬱陶しいものになることがあります。とはいえ
、デリミタ (delimiter) やクオート文字の 相違はあっても、全体的な形式は
十分似通っているため、こうしたデータを効率的に操作し、データの読み書き
における細々としたことをプログラマ から隠蔽するような単一のモジュール
を書くことは可能です。

"csv" モジュールでは、CSV 形式で書かれたテーブル状のデータを読み書きす
るためのクラスを実装しています。このモジュールを使うことで、プログラマ
は Excel で使われている CSV  形式に関して詳しい知識をもっていなくても
、 "このデータを Excel で推奨されている形式で書いてください" とか、 "
データを Excel で作成されたこのファイルから読み出してください" と言う
ことができます。プログラマはまた、他のアプリケーションが解釈できる CSV
形式を記述したり、独自の特殊な目的をもった CSV 形式を定義することがで
きます。

"csv" モジュールの "reader" および "writer" オブジェクトはシーケンス型
を読み書きします。プログラマは "DictReader" や "DictWriter" クラスを使
うことで、データを辞書形式で読み書きすることもできます。

注釈: このバージョンの "csv" モジュールは Unicode 入力をサポートして
  いませ ん。また、現在のところ、 ASCII NUL 文字に関連したいくつかの問
  題があ ります。従って、安全を期すには、全ての入力を UTF-8 または印字
  可能な ASCII にしなければなりません。これについては 使用例 節の例を
  参照して ください。

参考:

  **PEP 305** - CSV File API
     Python へのこのモジュールの追加を提案している Python 改良案 (PEP:
     Python Enhancement Proposal)。


13.1.1. モジュールコンテンツ
============================

"csv" モジュールでは以下の関数を定義しています:

csv.reader(csvfile, dialect='excel', **fmtparams)

   与えられた *csvfile* 内の行を反復処理するような reader  オブジェク
   トを返します。 *csvfile* はイテレータ(*iterator*)プロトコル をサポ
   ートし、 "next()" メソッドが呼ばれた際に常に文字列を返すような任意
   のオブジェクトにすることができます --- ファイルオブジェクトでも リ
   ストでも構いません。 *csvfile* がファイルオブジェクトの場合、ファイ
   ルオブジェクトの形式に違いがあるようなプラットフォームでは 'b' フラ
   グを付けて開かなければなりません。オプションとして *dialect* パラメ
   タを与えることができ、特定の CSV 表現形式 (dialect) 特有のパラメタ
   の集合を定義するために使われます。 *dialect* パラメタは "Dialect"
   クラスのサブクラス のインスタンスか、 "list_dialects()" 関数が返す
   文字列の一つにすることができます。別のオプションである *fmtparams*
   キーワード引数は、現在の表現形式における個々の書式パラメタを上書き
   するために与えることができます。表現形式および書式化パラメタの詳細
   については、 Dialect クラスと書式化パラメタ 節を参照してください。

   csv ファイルから読み込まれた各行は、文字列のリストとして返されます
   。 データ型の変換が自動的に行われることはありません。

   短い利用例:

      >>> import csv
      >>> with open('eggs.csv', 'rb') as csvfile:
      ...     spamreader = csv.reader(csvfile, delimiter=' ', quotechar='|')
      ...     for row in spamreader:
      ...         print ', '.join(row)
      Spam, Spam, Spam, Spam, Spam, Baked Beans
      Spam, Lovely Spam, Wonderful Spam

   バージョン 2.5 で変更: パーサが複数行に亘るクオートされたフィールド
   に関して厳格になりました。以前は、クオートされたフィールドの中で終
   端の改行文字無しに行が終わった場合、 返されるフィールドには改行が挿
   入されていましたが、この振る舞いはフィールドの中に復帰文字を含むよ
   うなファイルを読むときに問題を起こしていました。そこでフィールドに
   改行文字を挿入せずに返すように改められました。この結果、フィールド
   に埋め込まれた改行文字が重要ならば、入力は改行文字を保存するような
   仕方で複数行に分割されるはずです。

csv.writer(csvfile, dialect='excel', **fmtparams)

   ユーザが与えたデータをデリミタで区切られた文字列に変換し、与えられ
   たファイルオブジェクトに書き込むための writer オブジェクトを返しま
   す。 *csvfile* は "write()" メソッドを持つ任意のオブジェクトです。
   *csvfile* がファイルオブジェクトの場合、 'b' フラグが意味を持つプラ
   ットフォームでは 'b' フラグを付けて開かなければなりません。 オプシ
   ョンとして *dialect* 引数を与えることができ、利用する CSV 表現形式
   (dialect) を指定することができます。 *dialect* パラメタは "Dialect"
   クラスのサブクラスのインスタンスか、 "list_dialects()" 関数が返す文
   字列の 1 つにすることができます。 別のオプション引数である
   *fmtparams* キーワード引数は、現在の表現形式における個々の書式パラ
   メタを上書きするために与えることができます。 *dialect* と書式パラメ
   タについての詳細は、 Dialect クラスと書式化パラメタ 節を参照してく
   ださい。 DB API を実装するモジュールとのインタフェースを可能な限り
   容易にするために、 "None" は空文字列として書き込まれます。 この処理
   は可逆な変換ではありませんが、SQL で NULL データ値を CSV にダンプす
   る処理を、 "cursor.fetch*" 呼び出しによって 返されたデータを前処理
   することなく簡単に行うことができます。 浮動小数点数は、書き出される
   前に "repr()" を使って文字列に変換されます。 他の非文字列データは、
   書き出される前に "str()" を使って文字列に変換されます。

   短い利用例:

      import csv
      with open('eggs.csv', 'wb') as csvfile:
          spamwriter = csv.writer(csvfile, delimiter=' ',
                                  quotechar='|', quoting=csv.QUOTE_MINIMAL)
          spamwriter.writerow(['Spam'] * 5 + ['Baked Beans'])
          spamwriter.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])

csv.register_dialect(name[, dialect], **fmtparams)

   *dialect* を *name* と関連付けます。 *name* は文字列か Unicode オブ
   ジェクトでなければなりません。表現形式(dialect)は "Dialect" のサブ
   クラスを渡すか、またはキーワード引数 *fmtparams* 、もしくは両方で指
   定できますが、 キーワード引数の方が優先されます。表現形式と書式化パ
   ラメタについての詳細は、 Dialect クラスと書式化パラメタ 節を参照し
   てください。

csv.unregister_dialect(name)

   *name* に関連づけられた表現形式を表現形式レジストリから削除します。
   *name* が表現形式名でない場合には "Error" を送出します。

csv.get_dialect(name)

   *name* に関連づけられた表現形式を返します。 *name* が表現形式名でな
   い場合には "Error" を送出します。

   バージョン 2.5 で変更: この関数は *immutable* な "Dialect" クラスを
   返します。 以前は、要求された dialect のインスタンスが返されていま
   した。 ユーザーはクラスを操作すうることで、アクティブな reader や
   writer の動作を変更することができます。

csv.list_dialects()

   登録されている全ての表現形式を返します。

csv.field_size_limit([new_limit])

   パーサが許容する現在の最大フィールドサイズを返します。 *new_limit*
   が渡されたときは、その値が新しい上限になります。

   バージョン 2.5 で追加.

"csv" モジュールでは以下のクラスを定義しています:

class csv.DictReader(f, fieldnames=None, restkey=None, restval=None, dialect='excel', *args, **kwds)

   Create an object which operates like a regular reader but maps the
   information read into a dict whose keys are given by the optional
   *fieldnames* parameter.  The *fieldnames* parameter is a sequence
   whose elements are associated with the fields of the input data in
   order. These elements become the keys of the resulting dictionary.
   If the *fieldnames* parameter is omitted, the values in the first
   row of the file *f* will be used as the fieldnames.  If the row
   read has more fields than the fieldnames sequence, the remaining
   data is added as a sequence keyed by the value of *restkey*.  If
   the row read has fewer fields than the fieldnames sequence, the
   remaining keys take the value of the optional *restval* parameter.
   Any other optional or keyword arguments are passed to the
   underlying "reader" instance.

   短い利用例:

      >>> import csv
      >>> with open('names.csv') as csvfile:
      ...     reader = csv.DictReader(csvfile)
      ...     for row in reader:
      ...         print(row['first_name'], row['last_name'])
      ...
      Baked Beans
      Lovely Spam
      Wonderful Spam

class csv.DictWriter(f, fieldnames, restval='', extrasaction='raise', dialect='excel', *args, **kwds)

   Create an object which operates like a regular writer but maps
   dictionaries onto output rows.  The *fieldnames* parameter is a
   sequence of keys that identify the order in which values in the
   dictionary passed to the "writerow()" method are written to the
   file *f*.  The optional *restval* parameter specifies the value to
   be written if the dictionary is missing a key in *fieldnames*.  If
   the dictionary passed to the "writerow()" method contains a key not
   found in *fieldnames*, the optional *extrasaction* parameter
   indicates what action to take.  If it is set to "'raise'" a
   "ValueError" is raised. If it is set to "'ignore'", extra values in
   the dictionary are ignored. Any other optional or keyword arguments
   are passed to the underlying "writer" instance.

   Note that unlike the "DictReader" class, the *fieldnames* parameter
   of the "DictWriter" is not optional.  Since Python's "dict" objects
   are not ordered, there is not enough information available to
   deduce the order in which the row should be written to the file
   *f*.

   短い利用例:

      import csv

      with open('names.csv', 'w') as csvfile:
          fieldnames = ['first_name', 'last_name']
          writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

          writer.writeheader()
          writer.writerow({'first_name': 'Baked', 'last_name': 'Beans'})
          writer.writerow({'first_name': 'Lovely', 'last_name': 'Spam'})
          writer.writerow({'first_name': 'Wonderful', 'last_name': 'Spam'})

class csv.Dialect

   "Dialect" クラスはコンテナクラスで、基本的な用途としては、その属性
   を特定の "reader" や "writer" インスタンスのパラメタを定義するため
   に用います。

class csv.excel

   "excel" クラスは Excel で生成される CSV ファイルの通常のプロパティ
   を定義します。これは "'excel'" という名前の dialect として登録され
   ています。

class csv.excel_tab

   "excel_tab" クラスは Excel で生成されるタブ分割ファイルの通常のプロ
   パティを定義します。これは "'excel-tab'" という名前の dialect とし
   て登録されています。

class csv.Sniffer

   "Sniffer" クラスは CSV ファイルの書式を推理するために用いられるクラ
   スです。

   "Sniffer" クラスではメソッドを二つ提供しています:

   sniff(sample, delimiters=None)

      与えられた *sample* を解析し、発見されたパラメタを反映した
      "Dialect" サブクラスを返します。オプションの *delimiters* パラメ
      タを与えた場合、有効なデリミタ文字を含んでいるはずの文字列として
      解釈されます。

   has_header(sample)

      (CSV 形式と仮定される) サンプルテキストを解析して、最初の行がカ
      ラムヘッダの羅列のように推察される場合 "True" を返します。

"Sniffer" の利用例:

   with open('example.csv', 'rb') as csvfile:
       dialect = csv.Sniffer().sniff(csvfile.read(1024))
       csvfile.seek(0)
       reader = csv.reader(csvfile, dialect)
       # ... process CSV file contents here ...

"csv" モジュールでは以下の定数を定義しています:

csv.QUOTE_ALL

   "writer" オブジェクトに対し、全てのフィールドをクオートするように指
   示します。

csv.QUOTE_MINIMAL

   "writer" オブジェクトに対し、 *delimiter* 、 *quotechar* または
   *lineterminator* に含まれる任意の文字のような特別な文字を含むフィー
   ルドだけをクオートするように指示します。

csv.QUOTE_NONNUMERIC

   "writer" オブジェクトに対し、全ての非数値フィールドをクオートするよ
   うに指示します。

   "reader" に対しては、クオートされていない全てのフィールドを *float*
   型に変換するよう指示します。

csv.QUOTE_NONE

   "writer" オブジェクトに対し、フィールドを決してクオートしないように
   指示します。現在の *delimiter* が出力データ中に現れた場合、現在設定
   されている *escapechar* 文字が前に付けられます。 *escapechar* がセ
   ットされていない場合、エスケープが必要な文字に遭遇した writer は
   "Error" を送出します。

   "reader" に対しては、クオート文字の特別扱いをしないように指示します
   。

"csv" モジュールでは以下の例外を定義しています:

exception csv.Error

   全ての関数において、エラーが検出された際に送出される例外です。


13.1.2. Dialect クラスと書式化パラメタ
======================================

レコードに対する入出力形式の指定をより簡単にするために、特定の書式化パ
ラメタは表現形式 (dialect) にまとめてグループ化されます。表現形式は
"Dialect" クラスのサブクラスで、様々なクラス特有のメソッドと、
"validate()" メソッドを一つ持っています。 "reader" または "writer" オ
ブジェクトを生成するとき、プログラマは文字列または "Dialect" クラスの
サブクラスを表現形式パラメタとして渡さなければなりません。さらに、
*dialect* パラメタの代りに、プログラマは上で定義されている属性と同じ名
前を持つ個々の書式化パラメタを "Dialect" クラスに指定することができま
す。

Dialect は以下の属性をサポートしています:

Dialect.delimiter

   フィールド間を分割するのに用いられる 1 文字からなる文字列です。デフ
   ォルトでは "','" です。

Dialect.doublequote

   フィールド内に現れた *quotechar* のインスタンスで、クオートではない
   その文字自身でなければならない文字をどのようにクオートするかを制御
   します。 "True" の場合、この文字は二重化されます。 "False" の場合、
   *escapechar* は *quotechar* の前に置かれます。デフォルトでは "True"
   です。

   出力においては、 *doublequote* が "False" で *escapechar* がセット
   されていない場合、フィールド内に *quotechar* が現れると "Error" が
   送出されます。

Dialect.escapechar

   writer が、 *quoting* が "QUOTE_NONE" に設定されている場合に
   *delimiter* をエスケープするため、および、 *doublequote* が "False"
   の場合に *quotechar* をエスケープするために用いられる、 1 文字から
   なる文字列です。読み込み時には *escapechar* はそれに引き続く文字の
   特別な意味を取り除きます。デフォルトでは "None" で、エスケープを行
   ないません。

Dialect.lineterminator

   "writer" が作り出す各行を終端する際に用いられる文字列です。デフォル
   トでは "'\r\n'" です。

   注釈: "reader" は "'\r'" または "'\n'" のどちらかを行末と認識する
     ように ハードコードされており、 *lineterminator* を無視します。こ
     の振る 舞いは将来変更されるかもしれません。

Dialect.quotechar

   *delimiter* や *quotechar* といった特殊文字を含むか、改行文字を含む
   フィールドをクオートする際に用いられる 1 文字からなる文字列です。デ
   フォルトでは "'"'" です。

Dialect.quoting

   クオートがいつ writer によって生成されるか、また reader によって認
   識されるかを制御します。 "QUOTE_*" 定数のいずれか (モジュールコンテ
   ンツ 節参照) をとることができ、デフォルトでは "QUOTE_MINIMAL" です
   。

Dialect.skipinitialspace

   "True" の場合、 *delimiter* の直後に続く空白は無視されます。デフォ
   ルトでは "False" です。

Dialect.strict

   "True" の場合、 不正な CSV 入力に対して "Error" を送出します。デフ
   ォルトでは "False" です。


13.1.3. reader オブジェクト
===========================

reader オブジェクト("DictReader" インスタンス、および "reader()" 関数
によって返されたオブジェクト) は、以下の public なメソッドを持っていま
す:

csvreader.next()

   reader の反復可能なオブジェクトから、現在の表現形式に基づいて次の行
   を解析して返します。

reader オブジェクトには以下の公開属性があります:

csvreader.dialect

   パーサで使われる表現形式の読み取り専用の記述です。

csvreader.line_num

   ソースイテレータから読んだ行数です。この数は返されるレコードの数と
   は、レコードが複数行に亘ることがあるので、一致しません。

   バージョン 2.5 で追加.

DictReader オブジェクトは、以下の public な属性を持っています:

csvreader.fieldnames

   オブジェクトを生成するときに渡されなかった場合、この属性は最初のア
   クセス時か、ファイルから最初のレコードを読み出したときに初期化され
   ます。

   バージョン 2.6 で変更.


13.1.4. writer オブジェクト
===========================

"Writer" オブジェクト ("DictWriter" インスタンス、および "writer()" 関
数によって返されたオブジェクト) は、以下の public なメソッドを持ってい
ます: *row* には、 "Writer" オブジェクトの場合には文字列か数値のシーケ
ンスを指定し、 "DictWriter" オブジェクトの場合はフィールド名をキーとし
て対応する文字列か数値を格納した辞書オブジェクトを指定します(数値は
"str()" で変換されます)。複素数を出力する場合、値をかっこで囲んで出力
します。このため、CSV ファイルを読み込むアプリケーションで（そのアプリ
ケーションが複素数をサポートしていたとしても）問題が発生する場合があり
ます。

csvwriter.writerow(row)

   *row* パラメタを現在の表現形式に基づいて書式化し、 writer のファイ
   ルオブジェクトに書き込みます。

csvwriter.writerows(rows)

   Write all elements in *rows* (an iterable of *row* objects as
   described above) to the writer's file object, formatted according
   to the current dialect.

writer オブジェクトには以下の公開属性があります:

csvwriter.dialect

   writer で使われる表現形式の読み取り専用の記述です。

DictWriter のオブジェクトは以下の public メソッドを持っています:

DictWriter.writeheader()

   (コンストラクタで指定された)フィールド名の行を出力します。

   バージョン 2.7 で追加.


13.1.5. 使用例
==============

最も簡単な CSV ファイル読み込みの例です:

   import csv
   with open('some.csv', 'rb') as f:
       reader = csv.reader(f)
       for row in reader:
           print row

別の書式での読み込み:

   import csv
   with open('passwd', 'rb') as f:
       reader = csv.reader(f, delimiter=':', quoting=csv.QUOTE_NONE)
       for row in reader:
           print row

上に対して、単純な書き込みのプログラム例は以下のようになります。

   import csv
   with open('some.csv', 'wb') as f:
       writer = csv.writer(f)
       writer.writerows(someiterable)

新しい表現形式の登録:

   import csv
   csv.register_dialect('unixpwd', delimiter=':', quoting=csv.QUOTE_NONE)
   with open('passwd', 'rb') as f:
       reader = csv.reader(f, 'unixpwd')

もう少し手の込んだ reader の使い方 --- エラーを捉えてレポートします。

   import csv, sys
   filename = 'some.csv'
   with open(filename, 'rb') as f:
       reader = csv.reader(f)
       try:
           for row in reader:
               print row
       except csv.Error as e:
           sys.exit('file %s, line %d: %s' % (filename, reader.line_num, e))

このモジュールは文字列の解析は直接サポートしませんが、簡単にできます。

   import csv
   for row in csv.reader(['one,two,three']):
       print row

"csv" モジュールは直接は Unicode の読み書きをサポートしませんが、
ASCII NUL 文字に関わる問題のために8ビットクリーンに書き込みます。です
から、NUL を使う UTF-16 のようなエンコーディングを避ける限り エンコー
ド・デコードを行なう関数やクラスを書くことができます。 UTF-8 がお勧め
です。

以下の "unicode_csv_reader()" は Unicode の CSV データ (Unicode 文字列
のリスト)を扱うための "csv.reader" をラップするジェネレータ
(*generator*)です。 "utf_8_encoder()" は一度に 1 文字列(または行) ずつ
Unicode 文字列を UTF-8 としてエンコードするジェネレータです。エンコー
ドされた文字列は CSV reader により分解され、 "unicode_csv_reader()" が
UTF-8 エンコードの分解された文字列をデコードして Unicode に戻します。

   import csv

   def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
       # csv.py doesn't do Unicode; encode temporarily as UTF-8:
       csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
                               dialect=dialect, **kwargs)
       for row in csv_reader:
           # decode UTF-8 back to Unicode, cell by cell:
           yield [unicode(cell, 'utf-8') for cell in row]

   def utf_8_encoder(unicode_csv_data):
       for line in unicode_csv_data:
           yield line.encode('utf-8')

その他のエンコーディングには以下の "UnicodeReader" クラスと
"UnicodeWriter" クラスが使えます。二つのクラスは *encoding* パラメータ
をコンストラクタで取り、本物の reader や writer に渡されるデータが
UTF-8 でエンコードされていることを保証します。

   import csv, codecs, cStringIO

   class UTF8Recoder:
       """
       Iterator that reads an encoded stream and reencodes the input to UTF-8
       """
       def __init__(self, f, encoding):
           self.reader = codecs.getreader(encoding)(f)

       def __iter__(self):
           return self

       def next(self):
           return self.reader.next().encode("utf-8")

   class UnicodeReader:
       """
       A CSV reader which will iterate over lines in the CSV file "f",
       which is encoded in the given encoding.
       """

       def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
           f = UTF8Recoder(f, encoding)
           self.reader = csv.reader(f, dialect=dialect, **kwds)

       def next(self):
           row = self.reader.next()
           return [unicode(s, "utf-8") for s in row]

       def __iter__(self):
           return self

   class UnicodeWriter:
       """
       A CSV writer which will write rows to CSV file "f",
       which is encoded in the given encoding.
       """

       def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
           # Redirect output to a queue
           self.queue = cStringIO.StringIO()
           self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
           self.stream = f
           self.encoder = codecs.getincrementalencoder(encoding)()

       def writerow(self, row):
           self.writer.writerow([s.encode("utf-8") for s in row])
           # Fetch UTF-8 output from the queue ...
           data = self.queue.getvalue()
           data = data.decode("utf-8")
           # ... and reencode it into the target encoding
           data = self.encoder.encode(data)
           # write to the target stream
           self.stream.write(data)
           # empty queue
           self.queue.truncate(0)

       def writerows(self, rows):
           for row in rows:
               self.writerow(row)
