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

时间:2009-12-04 10:33:29

标签: python csv unicode utf-8 python-2.x

当涉及到UTF-8 / Unicode时,Python中的csv模块无法正常工作。我在Python documentation和其他网页上找到了适用于特定情况的代码段,但您必须了解正在处理的编码并使用相应的代码段。

如何从Python 2.6中“正常工作”的.csv文件中读取和写入字符串和Unicode字符串?或者这是Python 2.6的限制,没有简单的解决方案?

10 个答案:

答案 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)

我确认,unicodecsvcsv模块的绝佳替代品,我刚刚在源代码中将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)

有关其他讨论,请参阅:Does python 3.1.3 support unicode in csv module?

答案 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'参数传递给UnicodeCsvReaderUnicodeDictReader的构造函数来发出信号。使用BOM编译utf-8-sigutf-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.