【问题标题】:General Unicode/UTF-8 support for csv files in Python 2.6Python 2.6 中对 csv 文件的通用 Unicode/UTF-8 支持
【发布时间】:2010-12-23 04:48:16
【问题描述】:

当涉及 UTF-8/Unicode 时,Python 中的 csv 模块无法正常工作。我在Python documentation 和其他网页上发现了适用于特定情况的 sn-ps,但您必须充分了解您正在处理的编码并使用适当的 sn-p。

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

【问题讨论】:

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


    【解决方案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

    【讨论】:

      【解决方案2】:

      我会补充它的答案。默认情况下,excel 将 csv 文件保存为 latin-1(uccsv 不支持)。您可以通过以下方式轻松解决此问题:

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

      【讨论】:

        【解决方案3】:

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

        在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?

        【讨论】:

          【解决方案4】:

          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
          

          【讨论】:

          • 感谢这一点,它还包括超级有用的DictReader 接口,它允许您将 CSV 文件的每一行视为字典,其中键取自第一行的字段名:
          • 对于某些用途,您必须小心ucsv,因为它会尝试为您转换“看起来像数字”的数据。 (修改ucsv 不这样做很容易,但您只需要知道它就在那里。)
          • 我仍然收到 UnicodeDecodeError: 'utf8' codec can't decode byte 0xc9 in position 179: invalid continuation byte 形式的模块的 unicode 错误
          • @Mittenchops 听起来您的文件不是有效的 utf8。
          【解决方案5】:

          您应该考虑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。

          【讨论】:

            【解决方案6】:

            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
            

            【讨论】:

            • 对我来说效果很好。在为 Excel 保存时,我确实将 BOM('codecs.BOM_UTF8')添加到流的开头。 (我正在努力解决的唯一剩余问题是为什么我在保存然后重新加载时会丢失一些换行符,但这可能是我的错。)
            【解决方案7】:

            我确认,unicodecsvcsv 模块的绝佳替代品,我刚刚在我的源代码中将 csv 替换为 unicodecsv,它就像一个魅力。

            【讨论】:

            • 尝试为您同意的答案添加评论,而不是创建新答案(一旦您获得超过 50 个代表)
            • 我知道,但是我没有足够的声誉来回答一个答案:-(不过,你应该投票给我的答案以获得声誉;-)
            【解决方案8】:

            答案有点晚,但我使用unicodecsv 取得了巨大成功。

            【讨论】:

            • 我没有对它进行太多测试,但是这个包比@itsadok 的回答中提到的ucsv 有几个优点:(1)它完全打包用于生产,包括测试; (2) 它确实尝试只是将 Unicode 感知添加到 csv 模块,而不是默默地将它可以转换为数字的任何值(有点像 Excel)。诚然,有些人可能喜欢自动转换,但这不是 Python 的 csv 模块所打算的。
            • unicodecsv.DictReader 有一个巨大的陷阱。您必须将文件作为二进制文件打开,才能按预期使用 unicode。否则,当您访问给定行时,您仍然会收到 UnicodeEncodeError。 fileHandle = io.open(file_path, "rb") my_unicode_dictionary = unicodecsv.DictReader(fileHandle) row = my_unicode_dictionary.next()
            【解决方案9】:

            python documentation 中提到的包装器unicode_csv_reader 接受 Unicode 字符串。这是因为 csv 不接受 Unicode 字符串。 cvs 可能不知道编码或语言环境,只是将它获得的字符串视为字节。所以发生的事情是包装器对 Unicode 字符串进行编码,这意味着它创建了一个字节字符串。然后,当包装器返回来自 csv 的结果时,它会再次解码字节,这意味着它将 UTF-8 字节序列转换为正确的 unicode 字符。

            如果你给包装器一个纯字节字符串,例如通过使用f.readlines(),它将在值> 127的字节上给出UnicodeDecodeError。如果您的程序中有CSV格式的unicode字符串,您将使用包装器。

            我可以想象包装器仍然有一个限制:由于 cvs 不接受 unicode,而且它也不接受多字节分隔符,因此您无法解析以 unicode 字符作为分隔符的文件。

            【讨论】:

              【解决方案10】:

              那个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')
              

              【讨论】:

              • 在 linux 上对我不起作用: r = unicode_csv_reader(file('/tmp/csv-unicode.csv').read().split('\n')) ; r.next() 。给出:UnicodeDecodeError:'ascii'编解码器无法解码位置 14 中的字节 0xf8:序数不在范围内(128)
              猜你喜欢
              • 2014-10-04
              • 2012-09-29
              • 1970-01-01
              • 1970-01-01
              • 2015-04-12
              • 1970-01-01
              • 2011-03-08
              • 1970-01-01
              • 2018-01-17
              相关资源
              最近更新 更多