在Python3中,向io.BytesIO写入csv文件失败

26

我正在尝试编写适用于Python 2/3的兼容代码,以将字符串写入CSV文件对象。此代码:

line_as_list = [line.encode() for line in line_as_list]
writer_file =  io.BytesIO()
writer = csv.writer(writer_file, dialect=dialect, delimiter=self.delimiter)
for line in line_as_list:
    assert isinstance(line,bytes)
    writer.writerow(line)

在Python3中会出现以下错误:

>           writer.writerow(line)
E           TypeError: a bytes-like object is required, not 'str'

但是assert在类型上没有问题,那么为什么csv会创建错误?

我不能只针对Python 2和3使用BytesIO吗?这里的问题在哪里?


@tdelaney 我的意思是我不确定StringIO和BytesIO是否会为源文本(可能在utf-8中)提供相同的表示。这就是为什么我试图使用相同的输出对象类型。 - goelakash
1个回答

42
在Python3中,csv.writer要求以文本模式打开的类似文件的对象。而在Python2中,csv.writer则要求以二进制模式打开的类似文件的对象。因此,在Python3中使用io.StringIO,而在Python2中使用io.BytesIO
import io
import csv
import sys
PY3 = sys.version_info[0] == 3

line_as_list = [u'foo', u'bar']
encoding = 'utf-8'

if PY3:
    writer_file =  io.StringIO()
else:
    writer_file =  io.BytesIO()
    line_as_list = [line.encode(encoding) for line in line_as_list]

writer = csv.writer(writer_file, dialect='excel', delimiter=',')
writer.writerow(line_as_list)
content = writer_file.getvalue()

if PY3:
    content = content.encode(encoding)

print(type(content))
print(repr(content))
在Python3中,上面的代码会打印出:
<class 'bytes'>
b'foo,bar\r\n'

在Python2中,上述代码会打印出

<type 'str'>
'foo,bar\r\n'

这是一个不错的解决方法,但你有没有想过为什么错误提示要求“bytes”,而str本身就是字节格式呢? - goelakash
我相信错误来自于BytesIO对象--它抱怨传递了一个str而期望的是bytes。在Python3中,str不是“字节格式”。Unicode str是代码点序列。 - unutbu
但是我传递了一个 str.encode() 对象,实际上是一个字节对象。那么问题出在哪里呢?这个错误说传递了 str,但实际上并没有(只是在谈论 Python 3)。 - goelakash
我无法重现您发布的错误,所以这只是一个猜测。 self.delimiter 是什么?它可能是一个 str 吗? - unutbu
1
是的,可能是这样,但在编码分隔符后,它会显示“分隔符必须是字符串,而不是字节”。 - goelakash
显示剩余2条评论

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