Вопрос

Я пытаюсь перевести электронную таблицу Excel в CSV, используя модули Python xlrd и csv, но меня зацикливают на проблемах с кодировкой.Xlrd выдает выходные данные Excel в Юникоде, а для модуля CSV требуется UTF-8.

Я полагаю, что это не имеет никакого отношения к модулю xlrd:все работает нормально, выводя на стандартный вывод или другие выходные данные, которые не требуют определенной кодировки.

Рабочий лист имеет кодировку UTF-16-LE, согласно book.encoding

Упрощенная версия того, что я делаю:

from xlrd import *
import csv
b = open_workbook('file.xls')
s = b.sheet_by_name('Export')
bc = open('file.csv','w')
bcw = csv.writer(bc,csv.excel,b.encoding)
for row in range(s.nrows):
    this_row = []
    for col in range(s.ncols):
        this_row.append(s.cell_value(row,col))
    bcw.writerow(this_row)

Это приводит к следующей ошибке, около 740 строк:

UnicodeEncodeError: 'ascii' codec can't encode character u'\xed' in position 5: ordinal not in range(128)

Кажется, что значение зависает: «516-777316» — текст в исходном листе Excel — «516-7773167» (с цифрой 7 в конце).

Я буду первым, кто признает, что имею лишь смутное представление о том, как работает кодировка символов, поэтому большая часть того, что я пробовал до сих пор, представляет собой различные неуклюжие перестановки .encode и .decode на s.cell_value(row,col)

Если бы кто-то мог предложить решение, я был бы признателен - еще лучше, если бы вы могли объяснить, что не работает и почему, чтобы мне было легче самому отлаживать эти проблемы в будущем.

Заранее спасибо!

РЕДАКТИРОВАТЬ:

Спасибо за комментарии.

Когда я использую this_row.append(s.cell(row,col)) (например.s.cell вместо s.cell_value) весь документ пишет без ошибок.

Результат не особенно желателен (text:u'516-7773167'), но это позволяет избежать ошибки, даже если недопустимые символы все еще присутствуют в выводе.

Это заставляет меня думать, что проблема все-таки может быть в xlrd.

Мысли?

Это было полезно?

Решение

Я ожидаю cell_value возвращаемое значение — это строка в Юникоде, которая вызывает у вас проблемы (пожалуйста, распечатайте ее type() чтобы подтвердить это), и в этом случае вы сможете решить эту проблему, изменив одну строку:

this_row.append(s.cell_value(row,col))

к:

this_row.append(s.cell_value(row,col).encode('utf8'))

Если cell_value возвращает несколько разных типов, то вам нужно кодировать тогда и только тогда, когда он возвращает строку Юникода;поэтому вы разделите эту строку на несколько строк:

val = s.cell_value(row, col)
if isinstance(val, unicode):
    val = val.encode('utf8')
this_row.append(val)

Другие советы

Вы просили объяснений, но некоторые явления без вашей помощи необъяснимы.

(A) Строки в файлах XLS, созданных в Excel 97 и более поздних версиях, кодируются в Latin1, если возможно, в противном случае в UTF16LE.Каждая строка содержит флаг, указывающий, какая строка была использована.Раньше Excel кодировал строки в соответствии с «кодовой страницей» пользователя.В любом случае, xlrd создает объекты Юникода.Кодировка файла представляет интерес только в том случае, если файл XLS был создан сторонним программным обеспечением, которое либо опускает кодовую страницу, либо лжет о ней.См. раздел Unicode в начале документации xlrd.

(Б) Необъяснимое явление:

Этот код:

bcw = csv.writer(bc,csv.excel,b.encoding)

вызывает следующую ошибку в Python 2.5, 2.6 и 3.1: TypeError: expected at most 2 arguments, got 3 -- это примерно то, чего я ожидал, учитывая документацию по csv.writer;он ожидает файлоподобный объект, за которым следует либо (1) ничего, (2) диалект, либо (3) один или несколько параметров форматирования.Вы дали ему диалект, а у csv.writer нет аргумента кодировки, поэтому splat.Какую версию Python вы используете?Или вы не скопировали/вставили сценарий, который на самом деле запускали?

(C) Необъяснимые явления, связанные с отслеживанием, и каковы были фактические данные, нарушающие правила:

"the_script.py", line 40, in <module>
this_row.append(str(s.cell_value(row,col)))
UnicodeEncodeError: 'ascii' codec can't encode character u'\xed' in position 5: ordinal not in range(128) 

ВО-ПЕРВЫХ, в оскорбительной строке кода есть str(), которого не было в упрощенном сценарии - разве вы не скопировали/вставили сценарий, который вы на самом деле запускали?В любом случае вам не следует использовать str в целом — вы не получите полной точности своих чисел с плавающей запятой;просто позвольте модулю csv преобразовать их.

ВТОРОЕ, вы говорите """Кажется, что значение зацикливается на "516-777316" - текст в исходном листе Excel - "516-7773167" (с 7 на конце)""" - -- Трудно представить, как семерка теряется в конце.Я бы использовал что-то вроде этого, чтобы точно узнать, какие данные были проблемными:

try:
    str_value = str(s.cell_value(row, col))
except:
    print "row=%d col=%d cell_value=%r" % (row, col, s.cell_value(row, col))
    raise

Этот %r спасает вас от набора текста cell_value=%s ... repr(s.cell_value(row, col)) ...repr() создает однозначное представление ваших данных.Узнать его.Используй это.

Как вы пришли к номеру «516-777316»?

В ТРЕТЬИХ, сообщение об ошибке на самом деле жалуется на символ Юникода u'\xed' со смещением 5 (т.е.шестой символ).U+00ED — это ЛАТИНСКАЯ СТРОЧНАЯ БУКВА I С ОСТРЫМ, и ничего подобного в «516-7773167» вообще нет.

В ЧЕТВЕРТЫХ, местоположение ошибки кажется движущейся целью - вы сказали в комментарии к одному из решений:«Ошибка на bcw.writerow». Хм?

(D) Почему вы получили это сообщение об ошибке (с помощью str()): str(a_unicode_object) пытается преобразовать объект unicode в объект str и при отсутствии какой-либо информации о кодировке использует ascii, но у вас есть данные, отличные от ascii, поэтому splat.Обратите внимание, что ваша цель — создать файл csv, закодированный в utf8, но ваш упрощенный сценарий нигде не упоминает utf8.

(Э) """...s.cell(строка,столбец)) (например.s.cell вместо s.cell_value) весь документ пишет без ошибок.Результат не особенно желателен (текст: u'516-7773167')"""

Это происходит потому, что автор CSV вызывает __str__ метод вашего объекта Cell, и это создает <type>:<repr(value)> что может быть полезно для отладки, но, как вы говорите, не так уж хорошо в вашем CSV-файле.

(F) Решение Алекса Мартелли великолепно тем, что оно поможет вам начать работу.Однако вам следует прочитать раздел о классе Cell в документации xlrd:типы ячеек: текстовые, числовые, логические, даты, ошибки, пустые и пустые.Если у вас есть даты, вы захотите отформатировать их как даты, а не числа, поэтому вы не сможете использовать isinstance() (и, возможно, вам все равно не нужны накладные расходы на вызов функции)...это то, что Cell.ctype атрибут и Sheet.cell_type() и Sheet.row_types() методы предназначены для.

(G) UTF8 — это не Юникод.UTF16LE — это не Юникод.UTF16 - это не Юникод...и идея о том, что отдельные строки будут тратить по 2 байта каждая в спецификации UTF16, слишком нелепа, чтобы даже MS могла об этом подумать :-)

(H) Дальнейшее чтение (кроме документации xlrd):

http://www.joelonsoftware.com/articles/Unicode.html
http://www.amk.ca/python/howto/unicode

Похоже, у тебя 2 проблемы.

В этой ячейке что-то не так - я думаю, что «7» должно быть закодировано как u'x37, поскольку оно находится в диапазоне ASCII.

Но что еще более важно, тот факт, что вы получаете сообщение об ошибке, указывающее, что ascii кодек не может быть использован, предполагает, что что-то не так с вашей кодировкой в ​​Юникод - он думает, что вы пытаетесь закодировать значение 0xed это невозможно представить в ASCII, но вы сказали, что пытаетесь представить его в Юникоде.

Я недостаточно умен, чтобы определить, какая конкретная строка вызывает проблему. Если вы отредактируете свой вопрос, указав мне, какая строка вызывает это сообщение об ошибке, я смогу помочь немного больше (я думаю, это либо this_row.append(s.cell_value(row,col)) или bcw.writerow(this_row), но был бы признателен за подтверждение).

Кажется, есть две возможности.Во-первых, возможно, вы неправильно открыли выходной файл:

«Если CSVFile является объектом файла, он должен быть открыт с флагом« B »на платформах, где это имеет значение». ( http://docs.python.org/library/csv.html#module-csv )

Если проблема не в этом, другой вариант — использовать codecs.EncodedFile(file, input[, output[, error]]) в качестве оболочки для вывода вашего .csv:

http://docs.python.org/library/codecs.html#module-codecs

Это позволит вам использовать фильтр файловых объектов с входящего UTF16 на UTF8.Хотя оба они технически являются «Юникодом», способы их кодирования сильно различаются.

Что-то вроде этого:

rbc = open('file.csv','w')
bc = codecs.EncodedFile(rbc, "UTF16", "UTF8")
bcw = csv.writer(bc,csv.excel)

может решить проблему для вас, при условии, что я правильно понял проблему и при условии, что ошибка возникает при записи в файл.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top