web-dev-qa-db-ja.com

Noneおよび空の文字列でのcsvリーダーの動作

PythonのNoneモジュールを使用してPythonデータ構造とcsv表現の間を行き来するときに、csvと空の文字列を区別したいのですが。

私の問題は、私が実行すると:

import csv, cStringIO

data = [['NULL/None value',None],
        ['empty string','']]

f = cStringIO.StringIO()
csv.writer(f).writerows(data)

f = cStringIO.StringIO(f.getvalue())
data2 = [e for e in csv.reader(f)]

print "input : ", data
print "output: ", data2

次の出力が表示されます。

input :  [['NULL/None value', None], ['empty string', '']]
output:  [['NULL/None value', ''], ['empty string', '']]

もちろん、datadata2を使って、Noneと空の文字列を次のように区別できます。

data = [d if d!=None else 'None' for d in data]
data2 = [d if d!='None' else None for d in data2]

しかし、それはcsvモジュールに対する私の興味を部分的に無効にします(特に、大きなリストを扱う場合は、Cに実装された高速な逆シリアル化/シリアル化)。

このユースケースでcsv.DialectNoneを区別できるようにするcsv.writerおよびcsv.readerへの''またはパラメーターはありますか?

そうでない場合、csv.writerへのパッチを実装して、この種のやり取りを可能にすることに関心がありますか? (おそらく、下位互換性を確保するためにDialect.None_translate_toパラメータがデフォルトで''に設定されています)

21
user1509316

ドキュメント は、必要なものが不可能であることを示唆しています。

DB APIを実装するモジュールとのインターフェースをできるだけ簡単にするために、値Noneは空の文字列として書き込まれます。

これはwriterクラスのドキュメントにあり、すべての方言に当てはまり、csvモジュールの本質的な制限であることを示唆しています。

私はこれを変更することをサポートします(csvモジュールの他のさまざまな制限とともに)、この種の作業を別のライブラリにオフロードし、CSVモジュールをシンプルに(または少なくとも同じくらいシンプルに)したいと思うかもしれません。そのまま)。

より強力なファイル読み取り機能が必要な場合は、numpy、scipy、pandasのCSV読み取り関数を確認することをお勧めします。

12
BrenBarn

独自のバージョンのシングルトンcsvのようなクラス/値を作成することにより、Noneモジュールが行うことを少なくとも部分的に回避できます。

class NONE(object):
    def __repr__(self): # method csv.writer class uses to write values
        return 'NONE'   # unique string value to represent None
    def __len__(self):  # method called to determine length and truthiness
        return 0        # (optional)

NONE = NONE()  # singleton instance of the class

import csv
import cStringIO

data = [['None value', None], ['NONE value', NONE], ['empty string', '']]
f = cStringIO.StringIO()
csv.writer(f).writerows(data)
f = cStringIO.StringIO(f.getvalue())
print " input:", data
print "output:", [e for e in csv.reader(f)]

結果:

 input: [['None value', None], ['NONE value', NONE],   ['empty string', '']]
output: [['None value', ''],   ['NONE value', 'NONE'], ['empty string', '']]

NONEの代わりにNoneを使用すると、情報と実際の空の文字列データ値を区別できるように十分な情報が保持されます。

より良い代替案...
同じアプローチを使用して、比較的軽量のペアを実装することができますcsv.readerおよびcsv.writer「プロキシ」クラス— Cで記述された組み込みcsvクラスを実際にサブクラス化できないために必要です—多くのオーバーヘッドを導入せずに(処理の大部分は、基礎となるビルトイン)。これはすべてプロキシ内にカプセル化されているため、完全に透過的に行われます。

import csv

class csvProxyBase(object): _NONE = '<None>'  # unique value representing None

class csvWriter(csvProxyBase):
    def __init__(self, csvfile, *args, **kwrags):
        self.writer = csv.writer(csvfile, *args, **kwrags)
    def writerow(self, row):
        self.writer.writerow([self._NONE if val is None else val for val in row])
    def writerows(self, rows):
        map(self.writerow, rows)

class csvReader(csvProxyBase):
    def __init__(self, csvfile, *args, **kwrags):
        self.reader = csv.reader(csvfile, *args, **kwrags)
    def __iter__(self):
        return self
    def next(self):
        return [None if val == self._NONE else val for val in self.reader.next()]

if __name__ == '__main__':
    import cStringIO as StringIO
    data = [['None value', None], ['empty string', '']]
    f = StringIO.StringIO()
    csvWriter(f).writerows(data)
    f = StringIO.StringIO(f.getvalue())
    print " input:", data
    print "output:", [e for e in csvReader(f)]

結果:

 input: [['None value', None], ['empty string', '']]
output: [['None value', None], ['empty string', '']]
13
martineau

単なる方言であなたが望むことをすることは可能ではないと思いますが、あなた自身のcsv.reader/writeサブクラスを書くことができます。一方で、この使用例ではそれでもやり過ぎだと思います。 Noneだけではなく、str()だけを取得したい場合でも、

>>> data = [['NULL/None value',None],['empty string','']]
>>> i = cStringIO.StringIO()
>>> csv.writer(i).writerows(map(str,row) for row in data)
>>> print i.getvalue()
NULL/None value,None
empty string,
1
kojiro

シリアル化されたデータのコンシューマーと作成者の両方を制御できるので、その区別をサポートするフォーマットの使用を検討してください。

例:

>>> import json
>>> json.dumps(['foo', '', None, 666])
'["foo", "", null, 666]'
>>>
0
John Machin

上記のように、これはcsvモジュールの制限です。解決策は、次のように、ループ内の行を単純な辞書内包で書き換えるだけです。

reader = csv.DictReader(csvfile)
for row in reader:
    # Interpret empty values as None (instead of '')
    row = {k: v if v else None for k, v in row.items()}
    :
0
jrc

他の人が指摘したように、csv.Dialectまたはcsv.writercsv.readerへのパラメータを使用してこれを実際に行うことはできません。ただし、1つのコメントで述べたように、後者の2つを実質的にサブクラス化して実装します(組み込みなので、実際には実行できません)。書き込み時に「サブクラス」が行うことは、単にNone値をインターセプトし、それらを一意の文字列に変更し、それらを読み込むときにプロセスを逆にすることです。これが完全に機能する例です。

import csv, cStringIO
NULL = '<NULL>'  # something unlikely to ever appear as a regular value in your csv files

class MyCsvWriter(object):
    def __init__(self, *args, **kwrds):
        self.csv_writer = csv.writer(*args, **kwrds)

    def __getattr__(self, name):
        return getattr(self.csv_writer, name)

    def writerow(self, row):
        self.csv_writer.writerow([item if item is not None else NULL
                                      for item in row])
    def writerows(self, rows):
        for row in rows:
            self.writerow(row)

class MyCsvReader(object):
    def __init__(self, *args, **kwrds):
        self.csv_reader = csv.reader(*args, **kwrds)

    def __getattr__(self, name):
        return getattr(self.csv_reader, name)

    def __iter__(self):
        rows = iter(self.csv_reader)
        for row in rows:
            yield [item if item != NULL else None for item in row]

data = [['NULL/None value', None],
        ['empty string', '']]

f = cStringIO.StringIO()
MyCsvWriter(f).writerows(data)  # instead of csv.writer(f).writerows(data)

f = cStringIO.StringIO(f.getvalue())
data2 = [e for e in MyCsvReader(f)]  # instead of [e for e in csv.reader(f)]

print "input : ", data
print "ouput : ", data2

出力:

input :  [['NULL/None value', None], ['empty string', '']]
ouput :  [['NULL/None value', None], ['empty string', '']]

それは少し冗長で、おそらくcsvファイルの読み取りと書き込みを少し遅くします(C/C++で書かれているため)。

0
martineau

私もこの問題に遭遇し、これを見つけます https://bugs.python.org/issue23041

問題の解決策:

  • csv.DictWriterをサブクラス化し、要素タイプとして辞書を使用し、そのwriterowメソッドにアプリケーション固有の処理を実行させます。
  • 同様のことを行うwriterow()関数を定義します(本質的にはcsv.writerow()をラップします)。
0
XiYu Wang