当使用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', '']]
当然,我可以使用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]
但这会部分地打败我对csv
模块的兴趣(在C中实现的快速反序列化/序列化,特别是在处理大型列表时)。
csv.Dialect
和csv.writer
的{{1}}或参数是否可以让他们在此用例中区分csv.reader
和''
?< / p>
如果没有,是否有兴趣实施补丁None
来实现这种来回? (可能csv.writer
参数默认为Dialect.None_translate_to
以确保向后兼容性)
答案 0 :(得分: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)]
结果:
input: [['None value', None], ['NONE value', NONE], ['empty string', '']]
output: [['None value', ''], ['NONE value', 'NONE'], ['empty string', '']]
使用NONE
代替None
可以保留足够的信息,以便您能够区分它与任何实际的空字符串数据值。
更好的选择......
您可以使用相同的方法来实现一对相对轻量级的csv.reader
和csv.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)]
结果:
input: [['None value', None], ['empty string', '']]
output: [['None value', None], ['empty string', '']]
答案 1 :(得分:10)
The documentation表明你想要的东西是不可能的:
为了使与实现DB API的模块接口尽可能简单,将值None写为空字符串。
这是writer
类的文档,表明它适用于所有方言,并且是csv模块的内在限制。
我支持改变这个(以及csv模块的各种其他限制),但可能是人们想要将这种工作卸载到不同的库中,并保持CSV模块简单(或者至少这么简单。)
如果您需要更强大的文件读取功能,您可能需要查看numpy,scipy和pandas中的CSV读取功能,我记得有更多选项。
答案 2 :(得分: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,
答案 3 :(得分:0)
由于您可以控制序列化数据的使用者和创建者,因此请考虑使用支持该区别的格式。
示例:
>>> import json
>>> json.dumps(['foo', '', None, 666])
'["foo", "", null, 666]'
>>>
答案 4 :(得分:0)
正如其他人所指出的那样,你无法通过csv.Dialect
或csv.writer
和/或csv.reader
的参数来实现这一目标。但正如我在一篇评论中所说,你通过实现它实现继承后两者(你显然不能真正做到,因为它们是内置的)。 “子类”在写作时所做的只是拦截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绑定。
答案 5 :(得分:0)
我也遇到了这个问题,找到了这个https://bugs.python.org/issue23041。
问题解决方案:
- 子类csv.DictWriter,使用字典作为您的元素类型,并使用其writerow方法执行特定于应用程序的工作。
- 定义一个执行类似操作的writerow()函数(本质上包装csv.writerow())。
答案 6 :(得分:0)
如上所述,这是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()}
: