Python 2.6中对csv文件的一般Unicode / UTF-8支持

Python中的csv模块在涉及到UTF-8 / Unicode时无法正常工作。 我在Python文档和其他网页上发现了针对特定情况的片段,但您必须很好地理解您正在处理的编码以及使用合适的片段。

如何从Python 2.6中的“just works”中.csv文件中读取和写入string和Unicodestring? 还是这是Python 2.6的一个限制,没有简单的解决scheme?

如何阅读http://docs.python.org/library/csv.html#examples中给出的Unicode示例代码看起来已经过时,因为它不适用于Python 2.6和2.7。

这里跟UnicodeDictReader一起使用utf-8,可能与其他编码,但我只testing它在utf-8input。

简而言之,只有在csv行被csv.reader分割成字段后才能解码Unicode。

 class UnicodeCsvReader(object): def __init__(self, f, encoding="utf-8", **kwargs): self.csv_reader = csv.reader(f, **kwargs) self.encoding = encoding def __iter__(self): return self def next(self): # read and split the csv row into fields row = self.csv_reader.next() # now decode return [unicode(cell, self.encoding) for cell in row] @property def line_num(self): return self.csv_reader.line_num class UnicodeDictReader(csv.DictReader): def __init__(self, f, encoding="utf-8", fieldnames=None, **kwds): csv.DictReader.__init__(self, f, fieldnames=fieldnames, **kwds) self.reader = UnicodeCsvReader(f, encoding=encoding, **kwds) 

用法(源文件编码是utf-8):

 csv_lines = ( "абв,123", "где,456", ) for row in UnicodeCsvReader(csv_lines): for col in row: print(type(col), col) 

输出:

 $ python test.py <type 'unicode'> абв <type 'unicode'> 123 <type 'unicode'> где <type 'unicode'> 456 

有点迟到的答案,但我用unicodecsv取得了巨大的成功。

这里提供的模块,看起来像是一个很酷的简单的替代csv模块,允许您使用utf-8 csv。

 import ucsv as csv with open('some.csv', 'rb') as f: reader = csv.reader(f) for row in reader: print row 

那个doc中已经有了unicode的用法,为什么还要找另外一个或者重新发明轮子呢?

 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.reader(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] def utf_8_encoder(unicode_csv_data): for line in unicode_csv_data: yield line.encode('utf-8') 

我确认, unicodecsvcsv模块的一个很好的替代品,我刚刚用我的源代码中的unicodecsv代替了csv ,它就像一个魅力。

python文档中提到的包装unicode_csv_reader接受Unicodestring。 这是因为csv不接受Unicodestring。 CVS可能不知道编码或区域设置,只是将它作为字节获取的string。 那么会发生什么呢是包装器编码的Unicodestring,这意味着它创build一个字节的string。 然后,当包装器从csv返回结果时,它再次解码字节,这意味着它将UTF-8字节序列转换为正确的Unicode字符。

如果你给封装一个普通的字节string,例如使用f.readlines() ,它将给出一个值大于127的字节的UnicodeDecodeError 。如果你的程序中有Unicode格式的Unicodestring,你将使用封装。

我可以想象,包装仍然有一个限制:因为CVS不接受Unicode,也不接受多字节分隔符,您不能parsing有一个Unicode字符作为分隔符的文件。

你应该考虑tablib ,它有一个完全不同的方法,但应该在“正常工作”的要求下考虑。

 with open('some.csv', 'rb') as f: csv = f.read().decode("utf-8") import tablib ds = tablib.Dataset() ds.csv = csv for row in ds.dict: print row["First name"] 

警告:如果tablib在每一行上没有相同数量的项目,将会拒绝您的csv。

也许这是明显的,但为了初学者,我会提到它。

在python 3.X中csv模块支持任何开箱即用的编码 ,所以如果你使用这个版本,你可以坚持使用标准模块。

  with open("foo.csv", encoding="utf-8") as f: r = csv.reader(f, delimiter=";") for row in r: print(row) 

有关其他讨论,请参阅: python 3.1.3是否支持csv模块中的unicode?

这里是Maxim的答案略有改进的版本,也可以跳过UTF-8 BOM:

 import csv import codecs class UnicodeCsvReader(object): def __init__(self, csv_file, encoding='utf-8', **kwargs): if encoding == 'utf-8-sig': # convert from utf-8-sig (= UTF8 with BOM) to plain utf-8 (without BOM): self.csv_file = codecs.EncodedFile(csv_file, 'utf-8', 'utf-8-sig') encoding = 'utf-8' else: self.csv_file = csv_file self.csv_reader = csv.reader(self.csv_file, **kwargs) self.encoding = encoding def __iter__(self): return self def next(self): # read and split the csv row into fields row = self.csv_reader.next() # now decode return [unicode(cell, self.encoding) for cell in row] @property def line_num(self): return self.csv_reader.line_num class UnicodeDictReader(csv.DictReader): def __init__(self, csv_file, encoding='utf-8', fieldnames=None, **kwds): reader = UnicodeCsvReader(csv_file, encoding=encoding, **kwds) csv.DictReader.__init__(self, reader.csv_file, fieldnames=fieldnames, **kwds) self.reader = reader 

请注意, 不会自动检测到BOM的存在。 您必须通过将encoding='utf-8-sig'parameter passing给UnicodeCsvReaderUnicodeDictReader的构造函数来encoding='utf-8-sig'utf-8-sig编码是utf-8和BOM。

我会加上itsadok的答案。 默认情况下,Excel将csv文件保存为latin-1(ucsv不支持)。 你可以很容易地解决这个问题:

 with codecs.open(csv_path, 'rb', 'latin-1') as f: f = StringIO.StringIO( f.read().encode('utf-8') ) reader = ucsv.UnicodeReader(f) # etc.