当涉及到UTF-8 / Unicode时,Python中的csv模块无法正常工作。我在Python documentation和其他网页上找到了适用于特定情况的代码段,但您必须了解正在处理的编码并使用相应的代码段。
如何从Python 2.6中“正常工作”的.csv文件中读取和写入字符串和Unicode字符串?或者这是Python 2.6的限制,没有简单的解决方案?
答案 0 :(得分:52)
如何在http://docs.python.org/library/csv.html#examples处读取Unicode的示例代码看起来已经过时,因为它不适用于Python 2.6和2.7。
以下UnicodeDictReader
适用于utf-8,可能与其他编码有关,但我只在utf-8输入上测试过。
简而言之,只有在csv.reader
将csv行拆分为字段后才能解码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
答案 1 :(得分:32)
有点迟到的答案,但我使用unicodecsv取得了巨大的成功。
答案 2 :(得分:22)
模块提供了here,看起来像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
答案 3 :(得分:7)
在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')
答案 4 :(得分:4)
我确认,unicodecsv
是csv
模块的绝佳替代品,我刚刚在源代码中将csv
替换为unicodecsv
,它就像一个魅力。
答案 5 :(得分:3)
python documentation中提到的包装器unicode_csv_reader
接受Unicode字符串。这是因为csv不接受Unicode字符串。 cvs可能不知道编码或语言环境,只是将它获取的字符串视为字节。所以会发生的事情是包装器对Unicode字符串进行编码,这意味着它会创建一个字节串。然后,当包装器从csv返回结果时,它会再次解码字节,这意味着它将UTF-8字节序列转换为正确的unicode字符。
如果给包装器一个普通的字节字符串,例如使用f.readlines()
,它会在字节上显示UnicodeDecodeError
,其值为&gt; 127.如果您的程序中有Unicode格式的unicode字符串,您可以使用包装器。
我可以想象包装器仍然有一个限制:因为cvs不接受unicode,并且它也不接受多字节分隔符,所以你不能解析具有unicode字符作为分隔符的文件。
答案 6 :(得分:2)
你应该考虑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"]
警告:如果csv在每一行上没有相同数量的项目,它将拒绝你的csv。
答案 7 :(得分:2)
也许这显然很明显,但为了初学者,我会提到它。
在python 3.X csv
模块supports any encoding out of the box中,如果你使用这个版本,你可以坚持使用标准模块。
with open("foo.csv", encoding="utf-8") as f:
r = csv.reader(f, delimiter=";")
for row in r:
print(row)
答案 8 :(得分:1)
以下是Maxim's answer的略微改进版本,它也可以跳过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'
参数传递给UnicodeCsvReader
或UnicodeDictReader
的构造函数来发出信号。使用BOM编译utf-8-sig
为utf-8
。
答案 9 :(得分:0)
我想补充一下它的答案。默认情况下,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.