Python DictWriter 写入 UTF-8 编码的 CSV 文件

54 投票
6 回答
60385 浏览
提问于 2025-04-16 16:41
  1. 我有一个包含 Unicode 字符串的字典列表。
  2. csv.DictWriter 可以把字典列表写入一个 CSV 文件。
  3. 我希望这个 CSV 文件使用 UTF8 编码。
  4. 但是,csv 模块不能把 Unicode 字符串转换成 UTF8。
  5. csv 模块的文档中,有一个示例说明如何把所有内容转换成 UTF8:

    def utf_8_encoder(unicode_csv_data):
        for line in unicode_csv_data:
            yield line.encode('utf-8')
    
  6. 它还有一个 UnicodeWriter 类。

但是……我该怎么让 DictWriter 和这些一起工作呢?难道它们不需要在中间插入自己,以便在写入文件之前捕捉到被拆分的字典并进行编码吗?我不太明白。

6 个回答

15

你可以在把字典传给 DictWriter.writerow() 的时候,实时将值转换成 UTF-8 格式。比如说:

import csv

rows = [
    {'name': u'Anton\xedn Dvo\u0159\xe1k','country': u'\u010cesko'},
    {'name': u'Bj\xf6rk Gu\xf0mundsd\xf3ttir', 'country': u'\xcdsland'},
    {'name': u'S\xf8ren Kierkeg\xe5rd', 'country': u'Danmark'}
    ]

# implement this wrapper on 2.6 or lower if you need to output a header
class DictWriterEx(csv.DictWriter):
    def writeheader(self):
        header = dict(zip(self.fieldnames, self.fieldnames))
        self.writerow(header)

out = open('foo.csv', 'wb')
writer = DictWriterEx(out, fieldnames=['name','country'])
# DictWriter.writeheader() was added in 2.7 (use class above for <= 2.6)
writer.writeheader()
for row in rows:
    writer.writerow(dict((k, v.encode('utf-8')) for k, v in row.iteritems()))
out.close()

输出的文件叫 foo.csv

name,country
Antonín Dvořák,Česko
Björk Guðmundsdóttir,Ísland
Søren Kierkegård,Danmark
41

有一个简单的方法可以解决这个问题,使用很棒的UnicodeCSV模块。安装好这个模块后,只需要把这一行

import csv

改成

import unicodecsv as csv

这样就能自动很好地支持UTF-8编码了。

注意:如果你切换到Python 3,这个问题也会消失(感谢jamescampbell的建议)。而且其实切换到Python 3是个很好的选择。

101

更新: 第三方的 unicodecsv 模块已经实现了这个七年前的解决方案。下面的代码就是个例子。此外,还有一个不需要第三方模块的 Python 3 解决方案。

原始 Python 2 的回答

如果你使用的是 Python 2.7 或更高版本,可以用字典推导式把字典转换成 utf-8 格式,然后再传给 DictWriter:

# coding: utf-8
import csv
D = {'name':u'马克','pinyin':u'mǎkè'}
f = open('out.csv','wb')
f.write(u'\ufeff'.encode('utf8')) # BOM (optional...Excel needs it to open UTF-8 file properly)
w = csv.DictWriter(f,sorted(D.keys()))
w.writeheader()
w.writerow({k:v.encode('utf8') for k,v in D.items()})
f.close()

你可以用这个思路把 UnicodeWriter 更新为 DictUnicodeWriter:

# coding: utf-8
import csv
import cStringIO
import codecs

class DictUnicodeWriter(object):

    def __init__(self, f, fieldnames, dialect=csv.excel, encoding="utf-8", **kwds):
        # Redirect output to a queue
        self.queue = cStringIO.StringIO()
        self.writer = csv.DictWriter(self.queue, fieldnames, dialect=dialect, **kwds)
        self.stream = f
        self.encoder = codecs.getincrementalencoder(encoding)()

    def writerow(self, D):
        self.writer.writerow({k:v.encode("utf-8") for k,v in D.items()})
        # Fetch UTF-8 output from the queue ...
        data = self.queue.getvalue()
        data = data.decode("utf-8")
        # ... and reencode it into the target encoding
        data = self.encoder.encode(data)
        # write to the target stream
        self.stream.write(data)
        # empty queue
        self.queue.truncate(0)

    def writerows(self, rows):
        for D in rows:
            self.writerow(D)

    def writeheader(self):
        self.writer.writeheader()

D1 = {'name':u'马克','pinyin':u'Mǎkè'}
D2 = {'name':u'美国','pinyin':u'Měiguó'}
f = open('out.csv','wb')
f.write(u'\ufeff'.encode('utf8')) # BOM (optional...Excel needs it to open UTF-8 file properly)
w = DictUnicodeWriter(f,sorted(D.keys()))
w.writeheader()
w.writerows([D1,D2])
f.close()

Python 2 unicodecsv 示例:

# coding: utf-8
import unicodecsv as csv

D = {u'name':u'马克',u'pinyin':u'mǎkè'}

with open('out.csv','wb') as f:
    w = csv.DictWriter(f,fieldnames=sorted(D.keys()),encoding='utf-8-sig')
    w.writeheader()
    w.writerow(D)

Python 3:

另外,Python 3 自带的 csv 模块本身就支持 Unicode 格式:

# coding: utf-8
import csv

D = {u'name':u'马克',u'pinyin':u'mǎkè'}

# Use newline='' instead of 'wb' in Python 3.
with open('out.csv','w',encoding='utf-8-sig',newline='') as f:
    w = csv.DictWriter(f,fieldnames=sorted(D.keys()))
    w.writeheader()
    w.writerow(D)

撰写回答