Общая поддержка Unicode / UTF-8 для CSV-файлов в Python 2.6 - PullRequest
43 голосов
/ 04 декабря 2009

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

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

Ответы [ 10 ]

52 голосов
/ 31 мая 2011

Пример кода для чтения 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
32 голосов
/ 23 апреля 2012

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

22 голосов
/ 19 февраля 2012

Модуль, предоставленный здесь , выглядит как классная, простая замена для модуля 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
7 голосов
/ 04 декабря 2009

Пример использования Unicode уже используется в этом doc , почему все еще нужно найти другой или заново изобрести колесо?

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')
4 голосов
/ 19 сентября 2012

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

3 голосов
/ 27 июня 2011

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

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

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

2 голосов
/ 23 декабря 2013

Возможно, это очевидно, но ради новичков я упомяну это.

В 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?

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

1 голос
/ 14 декабря 2015

Вот слегка улучшенная версия ответа Максима , в которой также можно пропустить спецификацию 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 с спецификацией.

0 голосов
/ 30 июля 2015

Я бы добавил к его ответу. По умолчанию Excel сохраняет CSV-файлы как латиница-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.
...