使用None和空字符串的csv reader行为

use*_*316 19 python csv string nonetype

我想None在Python数据结构和使用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
Run Code Online (Sandbox Code Playgroud)

我得到以下输出:

input :  [['NULL/None value', None], ['empty string', '']]
output:  [['NULL/None value', ''], ['empty string', '']]
Run Code Online (Sandbox Code Playgroud)

当然,我可以玩data,并data2区分None和事情像空字符串:

data = [d if d!=None else 'None' for d in data]
data2 = [d if d!='None' else None for d in data2]
Run Code Online (Sandbox Code Playgroud)

但这部分会破坏我对csv模块的兴趣(在C中实现的快速反序列化/序列化,特别是在处理大型列表时).

是否有一个csv.Dialect或参数csv.writer,并csv.reader能够使他们之间的区别'',并None在此用例?

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

mar*_*eau 12

您可以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)]
Run Code Online (Sandbox Code Playgroud)

结果:

 input: [['None value', None], ['NONE value', NONE],   ['empty string', '']]
output: [['None value', ''],   ['NONE value', 'NONE'], ['empty string', '']]
Run Code Online (Sandbox Code Playgroud)

使用NONE而不是None保留足够的信息,以便能够区分它和任何实际的空字符串数据值.

即使是更好的选择......
你可以用同样的方法来实现对重量相对较轻的csv.readercsv.writer"代理"类-必须的,因为你不能真正继承内置csv这是写在C类-而不会引入大量的开销(因为大部分处理仍将由底层内置函数执行).这将使得完全透明,因为它全部封装在代理中.

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)]
Run Code Online (Sandbox Code Playgroud)

结果:

 input: [['None value', None], ['empty string', '']]
output: [['None value', None], ['empty string', '']]
Run Code Online (Sandbox Code Playgroud)

  • 第一个解决方案的变体为我解决了写入问题。创建了一个带有 __repr__ 的类 NONE(int),它返回一个空字符串。用 NONE 替换了所有 None 值(无论如何我都必须格式化我的数据,所以没有额外的工作)。然后使用 QUOTE_NONNUMERIC 创建 csv writer。这有点 hacky,但这意味着在输出文件中,您知道带引号的字段始终是字符串,而未带引号的空字段始终是 None。 (2认同)

Bre*_*arn 11

文档表明你想要的是不可能的:

为了使与实现DB API的模块接口尽可能简单,将值None写为空字符串.

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

我支持改变这个(以及csv模块的各种其他限制),但可能是人们想要将这种工作卸载到不同的库中,并保持CSV模块简单(或者至少简单因为它是).

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

  • 是的,确认了:查看 Modules/_csv.c 中的 csv_writerow ( if (field == Py_None) ... )。没有办法区分 '' 和 None。真的很遗憾,考虑到方言抽象,您本来希望有更多的灵活性。您提到 csv 模块的其他限制,您介意详细说明吗(如果还有其他问题,我真的应该开始查看其他 csv 阅读写作)? (3认同)