据我所知,Python (v2.6) 的 csv 模块默认无法处理 Unicode 数据,对吗?在 Python 文档中,有一个例子介绍如何从 UTF-8 编码的文件中读取。但这个例子只返回 CSV 行作为一个列表。
我想按名称访问行列,就像使用csv.DictReader
一样,但带有 UTF-8 编码的 CSV 输入文件。
有人能告诉我如何以有效的方式做到这一点吗?我需要处理数百兆大小的 CSV 文件。
我自己想出了一个答案:
def UnicodeDictReader(utf8_data, **kwargs):
csv_reader = csv.DictReader(utf8_data, **kwargs)
for row in csv_reader:
yield {unicode(key, 'utf-8'):unicode(value, 'utf-8') for key, value in row.iteritems()}
注:根据评论建议,现已更新以便解码密钥。
对我来说,关键不在于操纵 CSV DictReader 的参数,而是文件打开器本身。这个方法解决了问题:
with open(filepath, mode="r", encoding="utf-8-sig") as csv_file:
csv_reader = csv.DictReader(csv_file)
不需要特殊的类。现在我可以打开带或不带BOM的文件而不会崩溃。
基于类的方法来处理@LMatter的答案,使用这种方法,您仍然可以获得DictReader的所有好处,例如获取字段名称和行号,同时它还处理UTF-8。
import csv
class UnicodeDictReader(csv.DictReader, object):
def next(self):
row = super(UnicodeDictReader, self).next()
return {unicode(key, 'utf-8'): unicode(value, 'utf-8') for key, value in row.iteritems()}
csv.reader()
始终返回DictReader对象。import csv
def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
# csv.py doesn't do Unicode; encode temporarily as UTF-8:
csv_reader = csv.DictReader(utf_8_encoder(unicode_csv_data),
dialect=dialect, **kwargs)
for row in csv_reader:
# decode UTF-8 back to Unicode, cell by cell:
yield [unicode(cell, 'utf-8') for cell in row]
csv.reader()
在我的测试中没有返回DictReader对象。您确定吗?另外,您示例中的yield语句只返回具有值的列表而不是字典。 - LMattercsv.DictReader
而不是csv.reader
。请注意,除此之外,这直接来自文档。 - kelloti只需使用unicodecsv包就可以轻松实现。
# pip install unicodecsv
import unicodecsv as csv
with open('your_file.csv') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
print(row)
csvw
包还有其他功能(用于元数据丰富的Web CSV),但它定义了一个UnicodeDictReader
类,该类包装其UnicodeReader
类,其核心正是这样:
class UnicodeReader(Iterator):
"""Read Unicode data from a csv file."""
[…]
def _next_row(self):
self.lineno += 1
return [
s if isinstance(s, text_type) else s.decode(self._reader_encoding)
for s in next(self.reader)]
它有时确实会让我感到困惑,但是csvw.UnicodeDictReader
真的需要在with
块中使用,否则就会出错。除此之外,该模块非常通用,并且与py2和py3兼容。
答案中没有DictWriter
方法,因此这是更新后的类:
class DictUnicodeWriter(object):
def __init__(self, f, fieldnames, dialect=csv.excel, encoding="utf-8", **kwds):
self.fieldnames = fieldnames # list of keys for the dict
# 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, row):
self.writer.writerow({k: v.encode("utf-8") for k, v in row.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 row in rows:
self.writerow(row)
def writeheader(self):
header = dict(zip(self.fieldnames, self.fieldnames))
self.writerow(header)