如何将数据以字符串形式写入CSV格式(而不是文件)?

170

我想将像[1, 2, 'a', 'He said "what do you mean?"']这样的数据转换为CSV格式的字符串。

通常情况下,人们会使用csv.writer()来处理这个问题,因为它可以处理所有疯狂的边缘情况(逗号转义、引号转义、CSV方言等)。但问题在于csv.writer()期望输出到一个文件对象,而不是一个字符串。

我的当前解决方案是这个有点巧妙的函数:

def CSV_String_Writeline(data):
    class Dummy_Writer:
        def write(self,instring):
            self.outstring = instring.strip("\r\n")
    dw = Dummy_Writer()
    csv_w = csv.writer( dw )
    csv_w.writerow(data)
    return dw.outstring

是否有更加优雅的解决方案,仍能良好处理边缘情况?

编辑:这是我最终采用的方法:

def csv2string(data):
    si = StringIO.StringIO()
    cw = csv.writer(si)
    cw.writerow(data)
    return si.getvalue().strip('\r\n')

2
在Python 3中,StringIO()位于io库中。 - Aristide
由于"\r"和"\n"被视为空格,因此您实际上可以只使用不带参数的"strip":return si.getvalue().strip()——除非出于某种原因需要保留末尾的空格。 - MTKnife
7个回答

252

在Python 3中:

>>> import io
>>> import csv
>>> output = io.StringIO()
>>> csvdata = [1,2,'a','He said "what do you mean?"',"Whoa!\nNewlines!"]
>>> writer = csv.writer(output, quoting=csv.QUOTE_NONNUMERIC)
>>> writer.writerow(csvdata)
59
>>> output.getvalue()
'1,2,"a","He said ""what do you mean?""","Whoa!\nNewlines!"\r\n'

对于Python 2,需要稍微修改一些细节:

>>> output = io.BytesIO()
>>> writer = csv.writer(output)
>>> writer.writerow(csvdata)
57L
>>> output.getvalue()
'1,2,a,"He said ""what do you mean?""","Whoa!\nNewlines!"\r\n'

2
应该是 output = StringIO.StringIO()io.StringIO() 会引发 TypeError:期望字符串参数,得到了 'str'。 - Marboni
2
@Marboni:StringIO在Python 3中已经不存在(我的解决方案就是用Python 3编写的),而且我无法在Python 2.7.3中重现那个错误。不过我在writer.writerow(...)行中收到一个TypeError错误(需要unicode参数,但得到了'str')。我会继续研究这个问题。 - Tim Pietzcker
1
@Marboni:感谢你的提醒,我在StackOverflow上找到了答案。在Python 2中,需要使用io.BytesIO()而不是io.StringIO()。问题已解决。 - Tim Pietzcker
1
@Marboni:在Python 2.7.9中,可以使用StringIO.StringIO()或io.BytesIO()。 - srock
1
@nz_21:我刚刚重新测试了一下 - 完全正常。第二个示例(明确说明是为Python 2编写的)当然会抛出那个错误。 - Tim Pietzcker
显示剩余6条评论

73
你可以使用StringIO代替自己的Dummy_Writer:

这个模块实现了一个类似文件的StringIO类,它可以读写字符串缓冲区(也称为内存文件)。

还有cStringIO,这是StringIO类的更快版本。


3
cStringIO自Python 3.0起已经被废弃,请使用io.StringIO代替。 - Pei

9

由于我经常使用这种方式从 sanic 异步地将结果作为 csv 数据返回给用户,因此我为 Python 3 编写了以下代码片段。

这个代码片段使您可以反复重用相同的 StringIo 缓冲区。


import csv
from io import StringIO


class ArgsToCsv:
    def __init__(self, seperator=","):
        self.seperator = seperator
        self.buffer = StringIO()
        self.writer = csv.writer(self.buffer)

    def stringify(self, *args):
        self.writer.writerow(args)
        value = self.buffer.getvalue().strip("\r\n")
        self.buffer.seek(0)
        self.buffer.truncate(0)
        return value + "\n"

示例:

csv_formatter = ArgsToCsv()

output += csv_formatter.stringify(
    10,
    """
    lol i have some pretty
    "freaky"
    strings right here \' yo!
    """,
    [10, 20, 30],
)

可以参考 Github Gist 上的更多用法:源代码和测试


7

我觉得答案总的来说有点令人困惑。对于Python 2,以下使用方法适用于我:

import csv, io

def csv2string(data):
    si = io.BytesIO()
    cw = csv.writer(si)
    cw.writerow(data)
    return si.getvalue().strip('\r\n')

data=[1,2,'a','He said "what do you mean?"']
print csv2string(data)

0
这是适用于utf-8的版本。csvline2string用于单行,末尾没有换行符,csv2string用于多行,带有换行符:
import csv, io

def csvline2string(one_line_of_data):
    si = BytesIO.StringIO()
    cw = csv.writer(si)
    cw.writerow(one_line_of_data)
    return si.getvalue().strip('\r\n')

def csv2string(data):
    si = BytesIO.StringIO()
    cw = csv.writer(si)
    for one_line_of_data in data:
        cw.writerow(one_line_of_data)
    return si.getvalue()

0
我采用了上面@user2099484的答案,并对Unicode进行了小修改。
import csv, io

def csv2string(data):
    si = io.BytesIO()
    cw = csv.writer(si)
    data = [f.encode('utf-8') if type(f) == unicode else f for f in data]
    cw.writerow(data)
    return si.getvalue().strip('\r\n')

data=[1,2,'a','He said "what do you mean?"']
print csv2string(data)
data2=[1,2,u'שלום','He said "what do you mean?"']
print csv2string(data2)

-2
import csv
from StringIO import StringIO
with open('file.csv') as file:
    file = file.read()

stream = StringIO(file)

csv_file = csv.DictReader(stream)

4
只提供代码的答案是不被鼓励的,你应该在回答中添加一些解释来让答案更清晰易懂。 - Raniz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接