无字符串和空字符串的csv读取器行为

2024-04-25 02:29:47 发布

您现在位置:Python中文网/ 问答频道 /正文

在使用Python的csv模块在Python数据结构和csv表示之间来回切换时,我想区分None和空字符串。

我的问题是当我跑步时:

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.Dialectcsv.writercsv.reader的参数使它们能够区分''None

如果不是,是否有兴趣实现一个csv.writer补丁来实现这种来回?(可能是一个Dialect.None_translate_to参数,默认为'',以确保向后兼容)


Tags: 模块csv字符串innonefordatastring
3条回答

The documentation表明你想要的是不可能的:

To make it as easy as possible to interface with modules which implement the DB API, the value None is written as the empty string.

这在writer类的文档中,表明它适用于所有方言,并且是csv模块的内在限制。

我支持更改这个(以及csv模块的各种其他限制),但人们可能希望将这类工作卸载到不同的库中,并使csv模块保持简单(或至少尽可能简单)。

如果您需要更强大的文件读取功能,您可能需要查看numpy、scipy和pandas中的CSV读取功能,我记得它们有更多的选项。

我不认为仅仅用一种方言就可以做你想做的事情,但是你可以编写自己的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,

通过创建自己版本的类/值,您至少可以部分地避开csv模块所做的工作:

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.readercsv.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', '']]

相关问题 更多 >