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

我认为仅仅用方言不可能做你想做的事,但你可以编写自己的 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 回答
1

由于您可以控制序列化数据的使用者和创建者,因此请考虑使用支持这种区别的格式。

例子:

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

正如其他人指出的那样,您不能通过csv.Dialector 参数来真正做到这一点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 绑定的。

于 2012-07-09T04:23:06.107 回答
0

我也遇到了这个问题并找到了这个https://bugs.python.org/issue23041

问题的解决方案:

  • 子类 csv.DictWriter,使用字典作为元素类型,并让其 writerow 方法执行特定于应用程序的工作。
  • 定义一个 writerow() 函数,它执行类似的操作(本质上是包装 csv.writerow())。
于 2019-04-25T08:20:30.333 回答
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()}
    :
于 2019-11-14T14:27:01.170 回答