29

Pythonのモジュールを使ってPythonのデータ構造とcsv表現を行き来するときNone、空文字列( )と空文字列( )を区別​​したい。''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', '']]

もちろん、次のようなものを使用して、空の文字列を区別して区別するdataこともできます。data2None

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.Dialectへの または パラメータはありますか?csv.writercsv.reader''None

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

4

7 に答える 7

18

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

from __future__ import print_function
import csv


class NONE(object):
    ''' None-like class. '''
    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

NONE = NONE()  # Singleton instance of the class.


if __name__ == '__main__':

    try:
        from cStringIO import StringIO  # Python 2.
    except ModuleNotFoundError:
        from io import StringIO  # Python 3.

    data = [['None value', None], ['NONE value', NONE], ['empty string', '']]
    f = StringIO()
    csv.writer(f).writerows(data)

    f = 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すると、それと実際の空の文字列データ値を区別できるように十分な情報が保持されます。

さらに良い代替…</h3>

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

from __future__ import print_function
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):
        list(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 next(self.reader)]
    next = __next__  # Python2.x compatibility.


if __name__ == '__main__':

    try:
        from cStringIO import StringIO  # Python 2.
    except ModuleNotFoundError:
        from io import StringIO  # Python 3.

    data = [['None value', None], ['empty string', '']]
    f = StringIO()
    csvWriter(f).writerows(data)

    f = StringIO(f.getvalue())
    print("input : ", data)
    print("ouput : ", [e for e in csvReader(f)])

結果:

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

于 2012-07-08T01:52:00.643 に答える
17

ドキュメントは、あなたが望むことは不可能であることを示唆しています:

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

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

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

より強力なファイル読み取り機能が必要な場合は、numpy、scipy、およびpandasのCSV読み取り機能を確認することをお勧めします。これらには、より多くのオプションがあります。

于 2012-07-07T23:41:21.877 に答える
1

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

例:

>>> import json
>>> json.dumps(['foo', '', None, 666])
'["foo", "", null, 666]'
>>>
于 2012-07-07T23:29:11.157 に答える
1

単なる方言でやりたいことができるとは思いませんが、独自の 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,
于 2012-07-07T23:19:45.707 に答える
0

他の人が指摘しているように、csv.Dialectまたはパラメータを介してcsv.writerおよび/またはにこれを実際に行うことはできませんcsv.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 ++で記述されているため)が、プロセスはとにかく低レベルのI / Oバウンドである可能性が高いため、ほとんど違いはありません。

于 2012-07-09T04:23:06.107 に答える