Общая поддержка Unicode / UTF-8 для файлов csv в Python 2.6

Модуль csv в Python не работает должным образом при использовании UTF-8 / Unicode. Я нашел в документации Python и на других веб-страницах фрагменты, которые работают для определенных случаях, но вы должны хорошо понимать, с какой кодировкой вы работаете, и использовать соответствующий фрагмент.

Как я могу читать и записывать как строки, так и строки Unicode из файлов .csv, которые «просто работают» в Python 2.6? Или это ограничение Python 2.6, у которого нет простого решения?


person djen    schedule 04.12.2009    source источник


Ответы (10)


Пример кода чтения Unicode приведен на http://docs.python.org/library/csv.html#examples выглядит устаревшим, поскольку не работает с Python 2.6 и 2.7.

Здесь следует UnicodeDictReader, который работает с utf-8 и может быть с другими кодировками, но я тестировал его только на входах utf-8.

Вкратце идея состоит в том, чтобы декодировать Unicode только после того, как строка csv была разделена на поля с помощью csv.reader.

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
person Maxim Egorushkin    schedule 31.05.2011
comment
У меня сработало очень хорошо. Я добавил BOM ('codecs.BOM_UTF8') в начало потока при сохранении в Excel. (Единственная остаточная проблема, с которой я борюсь, - это то, почему я теряю некоторые разрывы строк при сохранении, а затем повторной загрузке, но это может быть моя ошибка.) - person Soferio; 04.10.2014

Немного запоздалый ответ, но я с большим успехом использовал unicodecsv.

person Serafeim    schedule 23.04.2012
comment
Я не тестировал его слишком много, но этот пакет имеет несколько преимуществ по сравнению с ucsv, упомянутым в ответе @ itsadok: (1) Он полностью упакован для производственного использования, включая тесты; и (2) он действительно пытается просто добавить поддержку Unicode в модуль csv, вместо того, чтобы молча преобразовывать любые значения, которые он может, в числа (что-то вроде Excel). Конечно, некоторым людям может быть нравиться автоматическое преобразование, но это не то, что модуль Python csv когда-либо предназначал. - person John Y; 28.02.2013
comment
У unicodecsv.DictReader есть одна большая проблема. Вы должны открыть файл как двоичный, чтобы он работал с Unicode должным образом. В противном случае вы все равно получите UnicodeEncodeError при доступе к данной строке. fileHandle = io.open (file_path, rb) my_unicode_dictionary = unicodecsv.DictReader (fileHandle) row = my_unicode_dictionary.next () - person Ed J; 18.11.2014

Модуль, представленный здесь, выглядит как классная, простая и легко заменяемая замена модуля 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
person itsadok    schedule 19.02.2012
comment
Спасибо за это, он также включает супер полезный интерфейс DictReader, который позволяет рассматривать каждую строку файла CSV как словарь, где ключи берутся из имен полей в первой строке: - person ; 14.04.2012
comment
В некоторых случаях вы должны быть осторожны с ucsv, так как он пытается преобразовать за вас числовые данные. (Достаточно легко изменить ucsv, чтобы этого не делать, но вы просто должны знать, что это есть.) - person John Y; 28.02.2013
comment
Я все еще получаю ошибки Unicode с этим модулем формы UnicodeDecodeError: 'utf8' codec can't decode byte 0xc9 in position 179: invalid continuation byte - person Mittenchops; 16.07.2013
comment
@Mittenchops Похоже, ваш файл недействителен utf8. - person itsadok; 17.07.2013

Пример 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')
person YOU    schedule 04.12.2009
comment
У меня не работает на linux: r = unicode_csv_reader (file ('/ tmp / csv-unicode.csv'). Read (). Split ('\ n')); r.next (). Выдает: UnicodeDecodeError: кодек ascii не может декодировать байт 0xf8 в позиции 14: порядковый номер не в диапазоне (128) - person Parand; 16.02.2011

Подтверждаю, unicodecsv - отличная замена модуля csv, я только что заменил csv на unicodecsv в моем исходном коде, и это работает как шарм.

person Ludovic Gasc - GMLudo    schedule 19.09.2012
comment
Попробуйте добавить комментарий к ответу, с которым вы согласны, вместо того, чтобы создавать новый ответ (когда у вас будет больше 50 реплик) - person Pierre GM; 19.09.2012
comment
Знаю, но у меня недостаточно репутации, чтобы отвечать под ответом :-( Тем не менее, вы должны проголосовать за мой ответ, чтобы заработать репутацию ;-) - person Ludovic Gasc - GMLudo; 24.09.2012

Оболочка unicode_csv_reader, упомянутая в документации Python, принимает строки Unicode. Это потому, что csv не принимает строки Unicode. cvs, вероятно, не знает кодировки или локали и просто обрабатывает полученные строки как байты. Итак, что происходит, оболочка кодирует строки Unicode, что означает, что она создает строку байтов. Затем, когда оболочка возвращает результаты из csv, она снова декодирует байты, что означает, что она преобразует последовательности байтов UTF-8 в правильные символы Unicode.

Если вы дадите оболочке простую байтовую строку, например. при использовании f.readlines() он выдаст UnicodeDecodeError байтов со значением> 127. Вы должны использовать оболочку, если в вашей программе есть строки Unicode в формате CSV.

Я могу представить, что оболочка все еще имеет одно ограничение: поскольку cvs не принимает Unicode, а также не принимает многобайтовые разделители, вы не можете анализировать файлы, которые имеют символ Unicode в качестве разделителя.

person gaston    schedule 27.06.2011

Может, это и очевидно, но для новичков упомяну.

В python 3.X csv модуль поддерживает любую кодировку из коробки , поэтому, если вы используете эту версию, вы можете придерживаться стандартного модуля.

 with open("foo.csv", encoding="utf-8") as f: 
     r = csv.reader(f, delimiter=";")
     for row in r: 
     print(row)

Дополнительное обсуждение см. На странице Поддерживает ли python 3.1.3 юникод в модуле csv?

person jb.    schedule 23.12.2013

Вам следует рассмотреть 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, если в каждой строке не будет одинакового количества элементов.

person itsadok    schedule 28.02.2013

Вот немного улучшенная версия ответа Максима, которая также может пропускать спецификацию UTF-8:

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

Обратите внимание, что наличие спецификации не определяется автоматически. Вы должны сообщить об этом, передав аргумент encoding='utf-8-sig' конструктору UnicodeCsvReader или UnicodeDictReader. Кодировка utf-8-sig - utf-8 со спецификацией.

person Matthias    schedule 13.12.2015

Я бы добавил к ответу itsadok. По умолчанию 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.
person user2426679    schedule 30.07.2015