Как я могу использовать io.StringIO() с модулем csv?

Я попытался выполнить резервное копирование программы Python 3 до версии 2.7, и я столкнулся со странной проблемой:

>>> import io
>>> import csv
>>> output = io.StringIO()
>>> output.write("Hello!")            # Fail: io.StringIO expects Unicode
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unicode argument expected, got 'str'
>>> output.write(u"Hello!")           # This works as expected.
6L
>>> writer = csv.writer(output)       # Now let try this with the csv module:
>>> csvdata = [u"Hello", u"Goodbye"]  # Look ma, all Unicode! (?)
>>> writer.writerow(csvdata)          # Sadly, no.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unicode argument expected, got 'str'

В соответствии с документами io.StringIO() возвращает поток в памяти для текста в Юникоде. Он работает корректно, когда я пытаюсь его комбинировать в строку Unicode вручную. Почему это происходит в сочетании с модулем csv, даже если все строки, которые записываются, являются строками Unicode? Откуда происходит str от причины Exception?

(я знаю, что я могу использовать StringIO.StringIO() вместо этого, но мне интересно, что случилось с io.StringIO() в этом сценарии)

Ответ 1

Модуль Python 2.7 csv не поддерживает ввод Unicode: см. примечание в начале документации.

Кажется, вам придется кодировать строки Unicode в байтовые строки и использовать io.BytesIO вместо io.StringIO.

В разделе examples документации приведены примеры для классов-оболочек UnicodeReader и UnicodeWriter (спасибо @AlexeyKachayev за указатель).

Ответ 2

Используйте StringIO.StringIO().

http://docs.python.org/library/io.html#io.StringIO

http://docs.python.org/library/stringio.html

io.StringIO - это класс. Он обрабатывает Unicode. Он отражает предпочтительную библиотечную структуру Python 3.

StringIO.StringIO - класс. Он обрабатывает строки. Он отражает устаревшую библиотечную структуру Python 2.

Ответ 3

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

import io
import csv

data = [[u'cell one', u'cell two'], [u'cell three', u'cell four']]

output = io.BytesIO()
writer = csv.writer(output, delimiter=',')
writer.writerows(data)
your_csv_string = output.getvalue()

Смотрите также

Ответ 4

Из csv документации:

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

Вы можете найти пример UnicodeReader, UnicodeWriter здесь http://docs.python.org/2/library/csv.html

Ответ 5

Чтобы использовать программу чтения/записи CSV с "файлами памяти" в Python 2.7:

from io import BytesIO
import csv

csv_data = """a,b,c
foo,bar,foo"""

# creates and stores your csv data into a file the csv reader can read (bytes)
memory_file_in = BytesIO(csv_data.encode(encoding='utf-8'))

# classic reader
reader = csv.DictReader(memory_file_in)

# writes a csv file
fieldnames = reader.fieldnames  # here we use the data from the above csv file
memory_file_out = BytesIO()     # create a memory file (bytes)

# classic writer (here we copy the first file in the second file)
writer = csv.DictWriter(memory_file_out, fieldnames)
for row in reader:
    print(row)
    writer.writerow(row)