Python 无和空字符串的CSV读取器行为
当使用Python的Python 无和空字符串的CSV读取器行为,python,csv,string,nonetype,Python,Csv,String,Nonetype,当使用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())
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',''']
输出:['NULL/None值','','','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.方言
或csv.writer
和csv.reader
的参数,使他们能够在本用例中区分'
和无
如果没有,是否有兴趣实现一个到
csv.writer
的补丁来实现这种来回转换?(可能是方言。无需将参数默认为'
,以确保向后兼容性。)我不认为仅仅用方言就可以做你想做的事情,但你可以编写自己的csv.reader/write子类。另一方面,我仍然认为这对于这个用例来说是过分的。即使您想要捕捉的不仅仅是None
,您也可能只想要str()
:
当您已经控制了串行数据的消费者和创建者时,请考虑使用支持该区别的格式。
例如:
>>> import json
>>> json.dumps(['foo', '', None, 666])
'["foo", "", null, 666]'
>>>
表明您想要的是不可能的:
为了尽可能方便地与实现DB API的模块进行接口,值None被写为空字符串
这在writer
类的文档中,表明它适用于所有方言,并且是csv模块的固有限制
就我个人而言,我支持改变这一点(以及csv模块的各种其他限制),但人们可能希望将这类工作转移到另一个库中,并保持csv模块的简单(或至少尽可能简单)
如果您需要更强大的文件读取功能,您可能想看看numpy、scipy和pandas中的CSV读取功能,我记得它们有更多的选项。您至少可以通过创建自己版本的类似类/值的singletonNone
,部分绕过CSV
模块的功能:
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)])
结果:
输入:[['None value',None],'None value',None],'empty string',''']
输出:['None value','','None value','None'],['empty string','']
使用NONE
而不是NONE
将保留足够的信息,以便您能够区分它和任何实际的空字符串数据值
更好的选择…
您可以使用相同的方法实现一对相对轻量级的csv.reader
和csv.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)])
正如其他人指出的那样,你不能通过csv.dialogue
或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
这有点冗长,可能会降低csv文件的读写速度(因为它们是用C/C++编写的),但这可能没有什么区别,因为这个过程可能是低级别I/O绑定的。我也遇到了这个问题,并发现了这个问题
问题的解决办法:
- 子类csv.DictWriter,使用字典作为元素类型,并让其writerow方法执行特定于应用程序的工作
- 定义一个writerow()函数,该函数执行类似的操作(本质上是包装csv.writerow())
如上所述,这是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()}
:
Yep确认:查看Modules/_csv.c中的csv_writerow(if(field==Py_None)…..。无法区分“”和None。真的很遗憾,考虑到方言抽象,您希望更灵活一些。您提到了csv模块的其他限制,您介意详细说明吗(如果还有其他问题,我真的应该开始考虑其他csv阅读和写作)?有时,我发现一个令人恼火的限制是分隔符必须是单个字符。因此,您无法解析列由两个制表符分隔的文件。就像您遇到的“无”一样,这很容易解决,但仍然令人恼火。另一个限制是模块内的硬编码ascii限制。第一个限制的变体是lution为我解决了写入问题。创建了一个类NONE(int)和一个返回空字符串的repr。将所有NONE值替换为NONE(我必须格式化数据,因此没有额外的工作)。然后用QUOTE_NONNUMERIC创建csv编写器。这有点老套,但这意味着在输出文件中,带引号的字段始终是字符串,而不带引号的空字段始终是无。@trellt
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()}
: