Python: преобразование Unicode в ASCII без ошибок для файла CSV

Я прочитал все вопросы о преобразовании из Unicode в CSV в Python здесь, в StackOverflow, и я все еще потерян. Каждый раз, когда я получаю сообщение "UnicodeEncodeError: кодек ascii не может кодировать символ u'\xd1' в позиции 12: порядковый номер не в диапазоне (128)"

buffer=cStringIO.StringIO()
writer=csv.writer(buffer, csv.excel)
cr.execute(query, query_param)
while (1):
    row = cr.fetchone()
    writer.writerow([s.encode('ascii','ignore') for s in row])

Значение row равно

(56, u"LIMPIADOR BA\xd1O 1'5 L")

где значение \xd10 в базе данных равно ñ, n с диакритической тильдой, используемой в испанском языке. Сначала я попытался преобразовать значение во что-то действительное в ascii, но, потеряв столько времени, я пытаюсь игнорировать только эти символы (полагаю, у меня была бы такая же проблема с гласными с ударением).

Я хотел бы сохранить значение в CSV, предпочтительно с ñ ("LIMPIADOR BAÑO 1'5 L"), но если это невозможно, по крайней мере иметь возможность сохранить его ("LIMPIADOR BAO 1'5 L").


person Sergi    schedule 10.01.2011    source источник
comment
Обновлено с вопросом в конце.   -  person Sergi    schedule 10.01.2011
comment
Почему бы вам не попробовать закодировать вашу локальную кодовую страницу Windows 'ANSI'? Я предполагаю, что вы используете Windows, потому что CSV чаще всего используется в Windows, но, пожалуйста, не обращайте на меня внимания, если это далеко от истины. Я предполагаю, что в ОС *NIX подойдет одна из 8-битных кодировок ISO, но я не эксперт.   -  person David Heffernan    schedule 10.01.2011


Ответы (1)


Правильно, ñ не является допустимым символом ASCII, поэтому вы не можете закодировать его в ASCII. Таким образом, вы можете, как и ваш код выше, игнорировать их. Другой способ, а именно убрать акценты, вы можете найти здесь: Как лучше всего удалить диакритические знаки в строке юникода Python?

Но обратите внимание, что оба метода могут привести к плохим последствиям, например, заставить слова на самом деле означать что-то другое и т. д. Так что лучше всего сохранить акценты. И тогда вы не можете использовать ASCII, но можете использовать другую кодировку. UTF-8 - безопасная ставка. Распространена латиница-1 или ISO-88591-1, но она включает только западноевропейские символы. CP-1252 распространен в Windows и т. д. и т. д.

Так что просто переключите «ascii» на любую кодировку, которую вы хотите.


Ваш фактический код, согласно вашему комментарию:

writer.writerow([s.encode('utf8') if type(s) is unicode else s for s in row]) 

где

row = (56, u"LIMPIADOR BA\xd1O 1'5 L")

Теперь я считаю, что это должно сработать, но, видимо, это не так. Я думаю, что unicode все равно передается в модуль записи cvs по ошибке. Разверните эту длинную строку на ее части:

col1, col2 = row # Use the names of what is actually there instead
row = col1, col2.encode('utf8')
writer.writerow(row) 

Теперь ваша настоящая ошибка не будет скрыта тем, что вы все втыкаете в одну строку. Этого также можно было бы избежать, если бы вы включили надлежащую трассировку.

person Lennart Regebro    schedule 10.01.2011
comment
Большинство читателей CSV не могут обрабатывать UTF-8. CSV чаще всего читается в Windows, поэтому одна из так называемых кодировок ANSI кажется наиболее подходящей. - person David Heffernan; 10.01.2011
comment
Ну, я не знаю о большинстве, но, по крайней мере, о некоторых. И везде используется CSV. Очевидно, что необходимо использовать кодировку, которая может быть прочитана целевым программным обеспечением. - person Lennart Regebro; 10.01.2011
comment
Дело в том, что приведенный выше код, использующий строку юникода uLIMPIADOR BA\xd1O 1'5 L, с треском проваливается с ошибкой UnicodeEncodeError, также описанной выше (флаг игнорирования не работает, я не знаю почему). В идеальных условиях я хотел бы получить файл CSV с полной строкой, включая ñ. - person Sergi; 11.01.2011
comment
@Sergi: Ну, это должно сработать, мне придется отлаживать ваш код, чтобы знать, что происходит. Но в любом случае: используйте лучшую кодировку, чем ascii. - person Lennart Regebro; 11.01.2011
comment
@Lennart: хорошо, как мне экспортировать их в CSV? Кодирование не работает даже при использовании utf-8. - person Sergi; 11.01.2011
comment
@Sergi: Тогда что-то еще не так. u"LIMPIADOR BA\xd1O 1'5 L".encode('utf8') здесь работает отлично. Как и u"LIMPIADOR BA\xd1O 1'5 L".encode('ascii', 'ignore'). Значит, что-то еще не так. Обратите внимание, что ваше утверждение о том, что строка (56, u"LIMPIADOR BA\xd1O 1'5 L"), не может быть правдой, так как вы получите 'int' object has no attribute 'encode', но это не та ошибка, о которой вы говорите. Значит что-то в вашем описании проблемы неверно. - person Lennart Regebro; 11.01.2011
comment
@ Леннарт, извините за задержку, ваш комментарий был скрыт. Вы правы, код в порядке, а ошибка была в другом месте. На самом деле проблема в том, что я написал writer.writerow([s.encode('utf8') if type(s) is str else s for s in row]) для кодирования только строк, тогда как на самом деле мне приходилось проверять if type(s) is unicode на юникод. Присуждены баллы. - person Sergi; 11.01.2011
comment
Хорошо, это немного странно, но я считаю, что ваша проблема в том, что вы передаете unicode в writerow(). Не впихивайте все в одну строчку кода, в этом нет смысла. :) - person Lennart Regebro; 11.01.2011
comment
@Lennart, вы правы, но думаю, что строка представляет собой кортеж, исходящий из запроса через fetchone в кортеже, поэтому я сделал это в одну строку, чтобы не копировать ее в список при перекодировании, а затем передавать значения в writerow (кстати, новичок в python;) - person Sergi; 11.01.2011