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')
我确认, unicodecsv
是csv
模块的一个很好的替代品,我刚刚用我的源代码中的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给UnicodeCsvReader
或UnicodeDictReader
的构造函数来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.