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

**ソースコード:** Lib/csv.py

======================================================================

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

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

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

参考:

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


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

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

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

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

   csv ファイルから読み込まれた各行は、文字列のリストとして返されます
   。"QUOTE_NONNUMERIC" フォーマットオプションが指定された場合を除き、
   データ型の変換が自動的に行われることはありません (このオプションが
   指定された場合、クォートされていないフィールドは浮動小数点数に変換
   されます)。

   短い利用例:

      >>> import csv
      >>> with open('eggs.csv', newline='') 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

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

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

   短い利用例:

      import csv
      with open('eggs.csv', 'w', newline='') 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* は文字列でなければなり
   ません。表現形式(dialect)は "Dialect" のサブクラスを渡すか、または
   キーワード引数 *fmtparams* 、もしくは両方で指定できますが、キーワー
   ド引数の方が優先されます。表現形式と書式化パラメータについての詳細
   は、 Dialect クラスと書式化パラメータ 節を参照してください。

csv.unregister_dialect(name)

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

csv.get_dialect(name)

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

csv.list_dialects()

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

csv.field_size_limit([new_limit])

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

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

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

   通常の reader のように動作しますが、個々の列の情報を "OrderedDict"
   にマップするオブジェクトを生成します。マップのキーは省略可能な
   *fieldnames* パラメータで与えられます。

   *fieldnames* パラメータは *sequence* です。 *fieldnames* が省略され
   た場合、ファイル *f* の最初の列の値が fieldnames として使われます。
   *fieldnames* がどのように決定されるかに関わらず、整列された
   (ordered) 辞書はそれらのオリジナルの順番を維持します。

   列が fieldnames より多くのフィールドを持っていた場合、残りのデータ
   はリストに入れられて、 *restkey* により指定されたフィールド名 (デフ
   ォルトでは "None") で保存されます。非ブランクの列が fieldnames より
   も少ないフィールドしか持たない場合、不明の値は "None" によって埋め
   られます。

   その他の省略可能またはキーワード形式のパラメータは、ベースになって
   いる "reader" インスタンスに渡されます。

   バージョン 3.6 で変更: 返される列の型は "OrderedDict" になりました
   。

   短い利用例:

      >>> import csv
      >>> with open('names.csv', newline='') as csvfile:
      ...     reader = csv.DictReader(csvfile)
      ...     for row in reader:
      ...         print(row['first_name'], row['last_name'])
      ...
      Eric Idle
      John Cleese

      >>> print(row)
      OrderedDict([('first_name', 'John'), ('last_name', 'Cleese')])

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

   通常の writer のように動作しますが、辞書を出力行にマップするオブジ
   ェクトを生成します。 *fieldnames* パラメータは、"writerow()" メソッ
   ドに渡された辞書の値がどのような順番でファイル *f* に書かれるかを指
   定するキーの "sequence" です。 "writerow()" メソッドに渡された辞書
   に *fieldnames* には存在しないキーが含まれている場合、オプションの
   *extrasaction* パラメータによってどんな動作を行うかが指定されます。
   この値がデフォルト値である "'raise'" に設定されている場合、
   "ValueError" が送出されます。 "'ignore'" に設定されている場合、辞書
   の余分な値は無視されます。その他のパラメータはベースになっている
   "writer" インスタンスに渡されます。

   "DictReader" クラスとは異なり、 "DictWriter" の *fieldnames* パラメ
   ータは省略可能ではありません。 Pythonの "dict" オブジェクトは整列さ
   れていないので、列をファイル *f* に書き込む際の順序を推定するために
   十分な情報が不足しているためです。

   短い利用例:

      import csv

      with open('names.csv', 'w', newline='') 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.unix_dialect

   "unix_dialect" クラスは UNIX システムで生成される CSV ファイルの通
   常のプロパティ (行終端記号として "'\n'" を用い全てのフィールドをク
   ォートするもの) を定義します。これは "'unix'" という名前の dialect
   として登録されています。

   バージョン 3.2 で追加.

class csv.Sniffer

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

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

   sniff(sample, delimiters=None)

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

   has_header(sample)

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

"Sniffer" の利用例:

   with open('example.csv', newline='') 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

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


14.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" です。


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

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

csvreader.__next__()

   reader の反復可能なオブジェクトから、現在の表現形式に基づいて次の行
   を解析してリスト（オブジェクトが "reader()" から返された場合）また
   は辞書 （ "DictReader" のインスタンスの場合）として返します。通常は
   "next(reader)" のようにして呼び出すことになります。

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

csvreader.dialect

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

csvreader.line_num

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

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

csvreader.fieldnames

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


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

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

csvwriter.writerow(row)

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

   バージョン 3.5 で変更: 任意のイテラブルのサポートの追加。

csvwriter.writerows(rows)

   *rows* 引数 (上で解説した *row* オブジェクトのイテラブル) の全ての
   要素を現在の表現形式に基づいて書式化し、writer のファイルオブジェク
   トに書き込みます。

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

csvwriter.dialect

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

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

DictWriter.writeheader()

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

   バージョン 3.2 で追加.


14.1.5. 使用例
==============

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

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

別の書式での読み込み:

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

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

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

"open()" が CSV ファイルの読み込みに使われるため、ファイルはデフォルト
ではシステムのデフォルトエンコーディングでユニコード文字列にデコードさ
れます ("locale.getpreferredencoding()" を参照)。他のエンコーディング
を用いてデコードするには、open の引数 "encoding" を設定して、以下のよ
うにします:

   import csv
   with open('some.csv', newline='', encoding='utf-8') as f:
       reader = csv.reader(f)
       for row in reader:
           print(row)

システムのデフォルトエンコーディング以外で書き込む場合も同様です。出力
ファイルを開く際に引数 "encoding" を明示してください。

新しい表現形式の登録:

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

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

   import csv, sys
   filename = 'some.csv'
   with open(filename, newline='') as f:
       reader = csv.reader(f)
       try:
           for row in reader:
               print(row)
       except csv.Error as e:
           sys.exit('file {}, line {}: {}'.format(filename, reader.line_num, e))

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

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

-[ 脚注 ]-

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