it-swarm-ru.tech

Python CSV DictReader с данными UTF-8

AFAIK, модуль Python (v2.6) csv не может обрабатывать данные Unicode по умолчанию, правильно? В документации по Python есть пример о том, как читать из файла в кодировке UTF-8. Но этот пример возвращает только строки CSV в виде списка .... Я хотел бы получить доступ к столбцам строк по имени, как это делается с помощью csv.DictReader, но с использованием входного файла CSV в кодировке UTF-8.

Может кто-нибудь сказать мне, как сделать это эффективным способом? Мне придется обрабатывать CSV-файлы размером в 100 МБ.

27
LMatter

Я сам придумал ответ:

def UnicodeDictReader(utf8_data, **kwargs):
    csv_reader = csv.DictReader(utf8_data, **kwargs)
    for row in csv_reader:
        yield {unicode(key, 'utf-8'):unicode(value, 'utf-8') for key, value in row.iteritems()}

Примечание: это было обновлено, поэтому ключи декодируются в соответствии с предложением в комментариях

48
LMatter

Классический подход к ответу @LMatter, благодаря этому подходу вы по-прежнему получаете все преимущества DictReader, такие как получение имен полей и номеров строк, а также UTF-8.

import csv

class UnicodeDictReader(csv.DictReader, object):

    def next(self):
        row = super(UnicodeDictReader, self).next()
        return {unicode(key, 'utf-8'): unicode(value, 'utf-8') for key, value in row.iteritems()}
0
Kasa

Прежде всего, используйте версию документации - . Это может измениться для каждого выпуска. В нем четко сказано, что он не поддерживает Unicode, но поддерживает UTF-8. Технически , это не одно и то же. Как говорят в документах:

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

В приведенном ниже примере (из документов) показано, как создать две функции, которые правильно читают текст как UTF-8 как CSV. Вы должны знать, что csv.reader() всегда возвращает объект DictReader.

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.DictReader(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]
0
kelloti

Ответ не имеет методов DictWriter, поэтому вот обновленный класс:

class DictUnicodeWriter(object):

    def __init__(self, f, fieldnames, dialect=csv.Excel, encoding="utf-8", **kwds):
        self.fieldnames = fieldnames    # list of keys for the dict
        # Redirect output to a queue
        self.queue = cStringIO.StringIO()
        self.writer = csv.DictWriter(self.queue, fieldnames, dialect=dialect, **kwds)
        self.stream = f
        self.encoder = codecs.getincrementalencoder(encoding)()

    def writerow(self, row):
        self.writer.writerow({k: v.encode("utf-8") for k, v in row.items()})
        # Fetch UTF-8 output from the queue ...
        data = self.queue.getvalue()
        data = data.decode("utf-8")
        # ... and reencode it into the target encoding
        data = self.encoder.encode(data)
        # write to the target stream
        self.stream.write(data)
        # empty queue
        self.queue.truncate(0)

    def writerows(self, rows):
        for row in rows:
            self.writerow(row)

    def writeheader(self):
        header = dict(Zip(self.fieldnames, self.fieldnames))
        self.writerow(header)
0
SePo

Пакет csvw также имеет другие функциональные возможности (для CSV для Web, обогащенных метаданными), но он определяет класс UnicodeDictReader, охватывающий его класс UnicodeReader, который в своей основе делает именно это:

class UnicodeReader(Iterator):
    """Read Unicode data from a csv file."""
    […]

    def _next_row(self):
        self.lineno += 1
        return [
            s if isinstance(s, text_type) else s.decode(self._reader_encoding)
            for s in next(self.reader)]

Это несколько раз меня зацепило, но csvw.UnicodeDictReaderдействительно, действительно нужно использовать в блоке with, и в противном случае он ломается. Кроме этого, модуль является достаточно универсальным и совместим с py2 и py3.

0
Anaphory