Читатель csv Python 2 и 3

Я пытаюсь использовать модуль csv для чтения файла csv utf-8, и у меня возникли проблемы с созданием общего кода для 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. Итак ... как мне это сделать?


person Syl    schedule 03.03.2011    source источник
comment
Итак, вам нужен код, который работает без изменений как на Python 2.7, так и на Python 3? Вероятно, невозможно, учитывая, что так много всего изменилось с обработкой строк и т. Д.   -  person Tim Pietzcker    schedule 03.03.2011
comment
можно ли указать код блока для Python 2 или 3?   -  person Syl    schedule 03.03.2011
comment
Вы можете проверить sys.version и обернуть свой код if - else выражением, да.   -  person Tim Pietzcker    schedule 03.03.2011
comment
@ Тим Пицчкер; лучше просить прощения, чем разрешения.   -  person Jakob Bowyer    schedule 03.03.2011
comment
Я думаю, что у вас был флаг b в неправильном примере, я его поменял.   -  person Lennart Regebro    schedule 03.03.2011
comment
@JakobBowyer EAFP работает только с именованными функциями, а не с выражениями генератора. Это сделано намеренно, и я могу сказать, что PEP 463 для встроенного отлова был отклоненный.   -  person Damian Yerrick    schedule 01.05.2017
comment
Хотя официальная рекомендация состоит в том, чтобы создавать CSV-файлы по-разному в Python 2 и Python 3, существует более чистый и элегантный способ, указанный как ответ на аналогичный, если не дубликат, вопрос.   -  person John Y    schedule 11.01.2018


Ответы (3)


Действительно, в 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:
    ...
person Lennart Regebro    schedule 03.03.2011
comment
Можете ли вы сделать with с дескриптором файла? - person Tim Pietzcker; 03.03.2011
comment
@Tim: это не дескриптор файла, это файловый объект, и вы можете делать with с файловыми объектами. Это именно то, что вы делаете, когда делаете with open(.... - person Lennart Regebro; 03.03.2011
comment
Имеет смысл. Вы никогда не увидите этого таким образом, это всегда with open(...) в документации, но этот способ неплохой - позволяет вам обернуть open() в блок try и поймать File not found и т. Д., Прежде чем передать его в блок with. - person Tim Pietzcker; 03.03.2011
comment
Или, в некоторых случаях, if sys.version < '3': open = codecs.open. - person agf; 13.05.2012
comment
@agf: Да, это тоже может сработать. codecs.open и Python 3 open - это не совсем одно и то же, хотя есть небольшие ловушки, но часто это срабатывает. Однако в версиях 2.6 и 2.7 вы можете делать from io import open. - person Lennart Regebro; 13.05.2012
comment
Читатель Python 2 CSV работает только с ASCII, поэтому использование r или rb для открытия может решить только часть проблемы. - person roskakori; 09.10.2016
comment
Это все еще правильный подход, даже с шестью или 2to3 и другими библиотеками? Я просто вижу, сколько лет этому ответу. - person Davos; 29.09.2017
comment
Насколько мне известно, да. - person Lennart Regebro; 05.10.2017

Обновление. Пока код в моем исходном ответе работает, я тем временем выпускаю небольшой пакет по адресу https://pypi.python.org/pypi/csv342, который предоставляет интерфейс, подобный Python 3, для Python 2. Таким образом, независимо от вашей версии 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
person roskakori    schedule 05.12.2014

Старый вопрос Я знаю, но я искал, как это сделать. На всякий случай, если кто-то подойдет к этому и может оказаться полезным.

Вот как я решил свою, спасибо Леннарту Регебро за подсказку. :

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:
       ......
person jscurtu    schedule 20.08.2013