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

问题描述 投票:0回答:6
  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
与这些一起工作?难道他们不需要将自己注入到其中,以捕获反汇编的字典并在将它们写入文件之前对其进行编码吗?我不明白。

python unicode csv utf-8
6个回答
102
投票

更新:第 3 方 unicodecsv 模块为您实现了这个 7 年前的答案。此代码下面的示例。还有一个不需要第三方模块的 Python 3 解决方案。

Python 2 原始答案

如果使用 Python 2.7 或更高版本,请在传递给 DictWriter 之前使用字典理解将字典重新映射为 utf-8:

# 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)

41
投票

有一个简单的解决方法,使用精彩的 UnicodeCSV 模块。有了之后,换线就可以了

import csv

import unicodecsv as csv

它会自动开始与 UTF-8 很好地配合。

注意:切换到Python 3也将解决这个问题(感谢jamescampbell的提示)。无论如何,这是一个人应该做的事情。


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

6
投票

您可以根据需要使用一些代理类来编码字典值,如下所示:

# -*- coding: utf-8 -*- 
import csv
d = {'a':123,'b':456, 'c':u'Non-ASCII: проверка'}

class DictUnicodeProxy(object):
    def __init__(self, d):
        self.d = d
    def __iter__(self):
        return self.d.__iter__()
    def get(self, item, default=None):
        i = self.d.get(item, default)
        if isinstance(i, unicode):
            return i.encode('utf-8')
        return i

with open('some.csv', 'wb') as f:
    writer = csv.DictWriter(f, ['a', 'b', 'c'])
    writer.writerow(DictUnicodeProxy(d))

2
投票

当您使用内容调用

csv.writer
时,其想法是通过
utf_8_encoder
传递内容,因为它会为您提供 (utf-8) 编码的内容。


1
投票

我的解决方案有点不同。虽然上述所有解决方案都专注于拥有 unicode 兼容的 dict,但我的解决方案使 DictWriter 与 unicode 兼容。 Python 文档中甚至建议使用这种方法 (1)。

类 UTF8Recoder、UnicodeReader、UnicodeWriter 取自 python 文档。 UnicodeWriter->writerow 也做了一些改变。

将其用作常规 DictWriter/DictReader。

这是代码:

import csv, codecs, cStringIO

class UTF8Recoder:
    """
    Iterator that reads an encoded stream and reencodes the input to UTF-8
    """
    def __init__(self, f, encoding):
        self.reader = codecs.getreader(encoding)(f)

    def __iter__(self):
        return self

    def next(self):
        return self.reader.next().encode("utf-8")

class UnicodeReader:
    """
    A CSV reader which will iterate over lines in the CSV file "f",
    which is encoded in the given encoding.
    """

    def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
        f = UTF8Recoder(f, encoding)
        self.reader = csv.reader(f, dialect=dialect, **kwds)

    def next(self):
        row = self.reader.next()
        return [unicode(s, "utf-8") for s in row]

    def __iter__(self):
        return self

class UnicodeWriter:
    """
    A CSV writer which will write rows to CSV file "f",
    which is encoded in the given encoding.
    """

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

    def writerow(self, row):
        self.writer.writerow([unicode(s).encode("utf-8") for s in row])
        # 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 row in rows:
            self.writerow(row)

class UnicodeDictWriter(csv.DictWriter, object):
    def __init__(self, f, fieldnames, restval="", extrasaction="raise", dialect="excel", *args, **kwds):
        super(UnicodeDictWriter, self).__init__(f, fieldnames, restval="", extrasaction="raise", dialect="excel", *args, **kwds)
        self.writer = UnicodeWriter(f, dialect, **kwds)
© www.soinside.com 2019 - 2024. All rights reserved.