【问题标题】:Python 2 and 3 csv readerPython 2 和 3 csv 阅读器
【发布时间】:2011-07-08 00:33:58
【问题描述】:

我正在尝试使用 csv 模块读取一个 utf-8 csv 文件,但由于编码,我在为 python 2 和 3 创建通用代码时遇到了一些麻烦。

这是 Python 2.7 中的原始代码:

with open(filename, 'rb') as csvfile:
    csv_reader = csv.reader(csvfile, quotechar='\"')
    langs = next(csv_reader)[1:]
    for row in csv_reader:
        pass

但是当我使用 python 3 运行它时,它不喜欢我在没有“编码”的情况下打开文件的事实。我试过这个:

with codecs.open(filename, 'r', encoding='utf-8') as csvfile:
    csv_reader = csv.reader(csvfile, quotechar='\"')
    langs = next(csv_reader)[1:]
    for row in csv_reader:
        pass

现在 python 2 无法解码“for”循环中的行。那么……我该怎么做呢?

【问题讨论】:

  • 所以您希望代码在 Python 2.7 和 3 上都保持不变?考虑到字符串处理等发生了很大变化,可能是不可能的。
  • 是否可以为 python 2 或 3 指定块代码?
  • 您可以检查sys.version 并在您的代码周围包裹if - else 语句,是的。
  • @Tim Pietzchker;请求原谅比请求许可更好。
  • 我认为你在错误的例子中使用了 b 标志,我把它换了。

标签: python encoding csv python-3.x


【解决方案1】:

更新:虽然我原始答案中的代码有效,但我同时在https://pypi.python.org/pypi/csv342 发布了一个小包,它为 Python 2 提供了类似 Python 3 的接口。因此,您可以简单地独立于您的 Python 版本做一个

import csv342 as csv
import io
with io.open('some.csv', 'r', encoding='utf-8', newline='') as csv_file:
    for row in csv.reader(csv_file, delimiter='|'):
        print(row)

原始答案:这是一个解决方案,即使使用 Python 2,它实际上也将文本解码为 Unicode 字符串,因此可以使用 UTF-8 以外的编码。

下面的代码定义了一个函数csv_rows(),它将文件的内容作为列表序列返回。示例用法:

for row in csv_rows('some.csv', encoding='iso-8859-15', delimiter='|'):
    print(row)

以下是csv_rows() 的两种变体:一种用于 Python 3+,另一种用于 Python 2.6+。在运行时,它会自动选择正确的变体。 UTF8RecoderUnicodeReaderexamples in the Python 2.7 library documentation 的逐字副本。

import csv
import io
import sys


if sys.version_info[0] >= 3:
    # Python 3 variant.
    def csv_rows(csv_path, encoding, **keywords):
        with io.open(csv_path, 'r', newline='', encoding=encoding) as csv_file:
            for row in csv.reader(csv_file, **keywords):
                yield row

else:
    # Python 2 variant.
    import codecs

    class UTF8Recoder:
        """
        Iterator that reads an encoded stream and reencodes the input to UTF-8
        """
        def __init__(self, f, encoding):
            self.reader = codecs.getreader(encoding)(f)

        def __iter__(self):
            return self

        def next(self):
            return self.reader.next().encode("utf-8")


    class UnicodeReader:
        """
        A CSV reader which will iterate over lines in the CSV file "f",
        which is encoded in the given encoding.
        """

        def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
            f = UTF8Recoder(f, encoding)
            self.reader = csv.reader(f, dialect=dialect, **kwds)

        def next(self):
            row = self.reader.next()
            return [unicode(s, "utf-8") for s in row]

        def __iter__(self):
            return self


    def csv_rows(csv_path, encoding, **kwds):
        with io.open(csv_path, 'rb') as csv_file:
            for row in UnicodeReader(csv_file, encoding=encoding, **kwds):
                yield row

【讨论】:

    【解决方案2】:

    确实,在 Python 2 中,文件应该以二进制模式打开,但在 Python 3 中则以文本模式打开。 Also in Python 3 newline='' should be specified(你忘了)。

    您必须在 if 块中打开文件。

    import sys
    
    if sys.version_info[0] < 3: 
        infile = open(filename, 'rb')
    else:
        infile = open(filename, 'r', newline='', encoding='utf8')
    
    
    with infile as csvfile:
        ...
    

    【讨论】:

    • 你能在文件句柄上做with吗?
    • @Tim:它不是文件句柄,它是文件对象,您可以对文件对象执行with。这正是你做with open(...时所做的事情。
    • 有道理。你从来没有真正看到它,它在文档中总是with open(...),但这种方式还不错 - 使您能够将open()包装在try块中并在处理之前捕获File not found等到with 块。
    • 或者,在某些情况下,if sys.version &lt; '3': open = codecs.open
    • @agf:是的,这也可以。 codecs.open 和 Python 3 open 并不完全相同,虽然有一些微妙的陷阱,但通常它会起作用。不过,在 2.6 和 2.7 中,您可以使用 from io import open
    【解决方案3】:

    我知道旧问题,但我正在研究如何做到这一点。以防万一有人过来发现它有用。

    这就是我解决问题的方法,感谢 Lennart Regebro 的提示。 :

    if sys.version > '3':
           rd = csv.reader(open(input_file, 'r', newline='',
           encoding='iso8859-1'), delimiter=';', quotechar='"')
    else:
           rd = csv.reader(open(input_file, 'rb'), delimiter=';',
           quotechar='"')
    

    然后做你需要做的:

    for row in rd:
           ......
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-28
      • 1970-01-01
      • 1970-01-01
      • 2016-10-26
      相关资源
      最近更新 更多