【问题标题】:Write non-Unicode using csv module使用 csv 模块编写非 Unicode
【发布时间】:2016-12-04 01:28:14
【问题描述】:

在迁移到 Python 3 时,我注意到我们使用内置 csv 生成的一些文件现在每个字符串周围都有 b' 前缀...

这里的代码应该根据export_fields 定义的一些参数为dogs 列表生成一个.csv(因此总是返回unicode 数据):

file_content = StringIO()
csv_writer = csv.writer(
    file_content, delimiter='\t', quotechar='"', quoting=csv.QUOTE_MINIMAL
)
csv_writer.writerow([
    header_name.encode('cp1252') for _v, header_name in export_fields
])
# Write content
for dog in dogs:
    csv_writer.writerow([
        get_value(dog).encode('cp1252') for get_value, _header in export_fields
    ])

问题是一旦我返回file_content.getvalue(),我得到:

b'Does he bark?'    b'Full     Name'    b'Gender'
b'Sometimes, yes'   b'Woofy the dog'    b'Male' 

而不是 (缩进已被修改为在 SO 上可读):

'Does he bark?'   'Full     Name'   'Gender'
'Sometimes, yes'  'Woofy the dog'   'Male' 

我在csv 模块中没有找到任何encoding 参数。我希望整个文件在 cp1252 中编码,所以我并不关心编码是通过行的迭代完成还是在文件本身上进行。

那么,有谁知道如何生成一个正确的字符串,只包含 cp1252 编码的字符串?

【问题讨论】:

  • 你为什么要编码?打开的文件对象负责处理。
  • @MartijnPieters 也许我的问题不完整:我想通过 Django 返回字符串:return HttpResponse(generate_csv_file())。我应该在 Django 级别处理编码吗?
  • 看我的回答;你在错误的水平上接近这个;制表符和引号字符也需要编码,但这是 I/O 级别的工作,而不是 csv 模块或生成行的代码。

标签: python python-3.x csv encoding stringio


【解决方案1】:

csv 模块处理文本,并使用str() 将任何不是字符串的内容转换为字符串。

不要传入bytes 对象。传入str 可以干净地转换为带有str() 的字符串的对象或类型。这意味着您不应编码字符串。

如果需要cp1252 输出,编码StringIO 值:

file_content.getvalue().encode('cp1252')

StringIO 对象也只处理文本。

更好的是,在csv 模块写入文件对象时,使用BytesIO object 和TextIOWrapper() 为您进行编码:

from io import BytesIO, TextIOWrapper

file_content = BytesIO()
wrapper = TextIOWrapper(file_content, encoding='cp1252', line_buffering=True)
csv_writer = csv.writer(
    wrapper, delimiter='\t', quotechar='"', quoting=csv.QUOTE_MINIMAL)

# write rows

result = file_content.getvalue()

我在包装器上启用了行缓冲,以便每次写入一行时它都会自动刷新到 BytesIO 实例。

现在file_content.getvalue() 产生一个字节串:

>>> from io import BytesIO, TextIOWrapper
>>> import csv
>>> file_content = BytesIO()
>>> wrapper = TextIOWrapper(file_content, encoding='cp1252', line_buffering=True)
>>> csv_writer = csv.writer(wrapper, delimiter='\t', quotechar='"', quoting=csv.QUOTE_MINIMAL)
>>> csv_writer.writerow(['Does he bark?', 'Full     Name', 'Gender'])
36
>>> csv_writer.writerow(['Sometimes, yes', 'Woofy the dog', 'Male'])
35
>>> file_content.getvalue()
b'Does he bark?\tFull     Name\tGender\r\nSometimes, yes\tWoofy the dog\tMale\r\n'

【讨论】:

  • 看起来它确实适用于包装器(一旦刷新,但您在我有时间发表评论之前进行了编辑)。测试通过了,所以 99% 确定它是正确的答案 :)
  • @MaximeLorant:我现在已将其切换为使用行缓冲;避免手动冲洗。对此感到抱歉。
  • 确实看起来更干净!感谢您的提示。
猜你喜欢
  • 2020-02-15
  • 1970-01-01
  • 2010-11-04
  • 1970-01-01
  • 2015-01-22
  • 2020-09-27
  • 1970-01-01
  • 2013-08-29
  • 2012-01-04
相关资源
最近更新 更多