Подтвердить что ты не робот

Unicode для UTF8 для файлов CSV - Python через xlrd

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

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

Рабочий лист кодируется как 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)

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

Спасибо заранее!

EDIT:

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

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

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

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

Мысли?

4b9b3361

Ответ 1

Я ожидаю, что возвращаемое значение cell_value является строкой unicode, которая дает вам проблемы (пожалуйста, распечатайте ее 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)

Ответ 2

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

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

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

Этот код:

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

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

(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() в строке кода нарушения, которая не была в упрощенном script - не скопировали/вставили script, который вы фактически запускали? В любом случае, вы не должны использовать str в целом - вы не получите полную точность своих поплавков; просто передайте модуль csv.

SECONDLY, вы говорите "" Значение, похоже, зацикливается на "516-777316" - текст в исходном листе Excel "516-7773167" (с 7 на конце) "" Трудно представить, как 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 - ЛАТИНСКАЯ МАЛЕНЬКАЯ ПИСЬМА я С ОСТРОМ, и ничего подобного в "516-7773167"

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

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

(E) ""... s.cell(строка, col)) (например, вместо s.cell_value) весь документ записывается без ошибок. Выход не является особенно желательным (текст: u'516- 7773167' ) ""

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

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

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

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

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

Ответ 3

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

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

Если это не проблема, другой вариант для вас - использовать codecs.EncodedFile(файл, input [, output [, errors]]) в качестве обертки для вывода вашего 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)

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

Ответ 4

Похоже, у вас есть 2 проблемы.

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

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

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