Python 2和3的CSV读取器

12

我正在尝试使用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" 循环中的行。那么我该怎么做?


3
你希望编写的代码可以同时在Python 2.7和3上运行且不需要修改吗?由于字符串处理等方面发生了如此多的变化,这可能是不可能的。 - Tim Pietzcker
能否为Python 2或3指定块代码? - Syl
2
你可以检查 sys.version 并在你的代码周围包装一个 if - else 语句。 - Tim Pietzcker
@Tim Pietzchker;宁愿先行动再征得许可,胜过事事都要征求许可。 - Jakob Bowyer
虽然在Python 2和Python 3中“官方”的建议是以不同的方式处理CSV,但是在类似或重复的问题的答案中列出了一种更加简洁、优雅的方法。链接如下:https://dev59.com/ZlkT5IYBdhLWcg3wStof#39379062 - John Y
显示剩余2条评论
3个回答

17

事实上,在Python 2中,文件应该以二进制模式打开,但在Python 3中则应该以文本模式打开。同样在Python 3中,应指定newline=''(您忘了)参见此处

您需要在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:
    ...

1
@Tim:它不是文件句柄,而是文件对象,你可以在文件对象上使用with。这正是当你使用with open(...时所做的。 - Lennart Regebro
3
有道理。通常在文档中我们总是看到 with open(...),但这种方式也不错——它使你能够将 open() 包装在 try 块中并捕获 File not found 等异常,然后再将其传递给 with 块。 - Tim Pietzcker
1
或者在某些情况下,if sys.version < '3': open = codecs.open - agf
2
@agf:是的,那也可以。codecs.open和Python 3 open并不完全相同,因此存在一些微妙的陷阱,但通常情况下它会起作用。在2.6和2.7中,您可以执行from io import open - Lennart Regebro
即使有6或2to3和其他库,这仍然是正确的方法吗?我只是看到这个答案有多老。 - Davos
显示剩余3条评论

2
更新:尽管我原先回答中的代码是可行的,但我现在已经发布了一个小包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+。在运行时它会自动选择正确的变体。`UTF8Recoder`和`UnicodeReader`是 Python 2.7库文档中的示例的逐字拷贝。
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

0

虽然这是一个老问题,但我正在寻找如何解决它。以防万一有人看到了这篇文章并发现它有用。

这是我解决问题的方法,感谢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:
       ......

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接