Почему я получаю ошибку кодирования в python warnings.formatwarning в строке формата?

Я получаю ошибку кодирования в этой строке:

s =  "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message)

UnicodeEncodeError: кодек ascii не может кодировать символ u '\ xc4' в позиции 44: порядковый номер не в диапазоне (128)

Я попытался воспроизвести эту ошибку, передав все комбинации параметров в строковый формат, но ближе всего к мне была ошибка «декодирования ascii» (путем одновременной передачи строки unicode и high ascii, что привело к принудительному преобразованию строки в unicode с использованием декодера ascii.

Однако мне не удалось получить ошибку "ascii encode". У кого-нибудь есть идея?


person Community    schedule 06.10.2009    source источник
comment
О, вы понимаете, когда вызывается warnings.warn ... Разве вы не могли так сказать? Было непонятно, что это не ваш код, а в стандартной библиотеке. Вы должны говорить, в чем заключается ваша проблема, а не на общий вопрос, который, по вашему мнению, является проблемой, потому что обычно это не так. Я обновил свой ответ ниже, добавив более подробную информацию.   -  person Lennart Regebro    schedule 06.10.2009


Ответы (3)


Это происходит, когда Python пытается заставить аргумент:

s = u"\u00fc"
print str(s)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xfc' in position 0: ordinal not in range(128)

Это происходит потому, что один из ваших аргументов является объектом (а не строкой какого-либо вида), и Python вызывает str() для него. Есть два решения: используйте строку Unicode для формата (s = u"%s...") или заключите каждый аргумент в repr().

person Aaron Digulla    schedule 06.10.2009
comment
Формат находится в модуле предупреждений, поэтому я бы не стал его менять, но добавление repr () вокруг переданных параметров звучит очень хорошо. Спасибо! - person ; 06.10.2009
comment
Тогда вы получите дополнительные цитаты и дополнительный u. Работает как хак, но не очень красиво. - person Lennart Regebro; 06.10.2009
comment
Ошибка возникает, когда предупреждение, выдаваемое базой данных, фиксируется для ведения журнала. Поскольку регистрация не удалась, я полностью в неведении относительно исходной проблемы, что является наихудшим местом. Мне нравятся мои журналы, читаемые следующим человеком, поэтому я решил обернуть форматирование в блоке try: except:, сделав это сначала хорошо и используя repr () только в случае ошибки кодирования, включая выдачу дополнительного предупреждения о проблема с кодировкой. ИМХО, это не взлом, а лучше, безопаснее, логгирование. - person ; 06.10.2009
comment
Ведение журнала должно быть безопасным для Unicode, но, вероятно, это верно только для Python 3. - person Aaron Digulla; 06.10.2009
comment
Я считаю, что Unicode безопасен в Python 2.x, если вы знаете, что делаете (это обычно относится к Unicode и Python 2.x, а не только к журналированию). Любые сообщения в байтах (например, объекты str, а не Unicode) необходимо преобразовать в Unicode с использованием соответствующей кодировки, иначе вы столкнетесь с проблемами такого рода, вызванными неправильным смешиванием str и unicode. Обработчики ведения журнала на основе файлов позволяют указать кодировку, а обработчики на основе потоков могут принимать поток, вокруг которого есть оболочка кодирования. - person Vinay Sajip; 06.10.2009
comment
@VinaySajip на самом деле не работает в Python 2.x: import warnings; warnings.warn(u'Предупреждение') поднимает UnicodeEncodeError - person Ivan Virabyan; 14.01.2014
comment
@Ivan Virabyan - warnings на самом деле другой модуль, чем logging :-) - person Vinay Sajip; 14.01.2014
comment
да, но исходный вопрос был именно о модуле предупреждения, я думал, вы тоже об этом говорите - person Ivan Virabyan; 14.01.2014

Вы смешиваете объекты Unicode и str.

Объяснение: В Python 2.x есть два типа объектов, которые могут содержать текстовые строки. str и юникод. str - это строка байтов, поэтому она может содержать только символы от 0 до 255. Юникод - это строка символов Юникода.

Вы можете конвертировать между str и unicode с помощью методов "encode" и "decode":

>>> "thisisastring".decode('ascii')
u'thisisastring'

>>> u"This is ä string".encode('utf8')    
'This is \xc3\xa4 string'

Обратите внимание на кодировки. Кодировки - это способы представления текста в Юникоде только в виде строк байтов.

Если вы попытаетесь добавить str и unicode вместе, Python попытается преобразовать один в другой. Но по умолчанию он будет использовать ASCII в качестве кодировки, что означает a-z, A-Z и некоторые дополнительные символы, такие как !"#$%&/()=?'{[]]} и т. Д. Все остальное не удастся.

В этот момент вы получите либо ошибку кодирования, либо ошибку декодирования, в зависимости от того, пытается ли Python преобразовать unicode в str или str в unicode. Обычно он пытается декодировать, то есть конвертировать в юникод. Но иногда решает не натягивать, а принуждать к веревке. Я не совсем уверен, почему.

Обновление: причина, по которой вы получаете ошибку кодирования, а не ошибку декодирования выше, заключается в том, что message в приведенном выше коде не является ни str, ни unicode. Это еще один объект, у которого есть метод str. Поэтому Python выполняет str (message) перед его передачей, и это не удается, поскольку внутренне хранимое сообщение является объектом unicode, который нельзя принудительно преобразовать в ascii.

Или, проще говоря: он не работает, потому что warnings.warn () не принимает сообщения Unicode.

Теперь решение:

Не смешивайте str и unicode. Если вам нужно использовать юникод, а вы, очевидно, это делаете, постарайтесь, чтобы все строки всегда были в юникоде. Это единственный способ избежать этого. Это означает, что всякий раз, когда вы читаете строку с диска или вызываете функцию, которая может возвращать что-либо, кроме чистой ascii str, декодируйте ее в Unicode как можно скорее. И когда вам нужно сохранить его на диск, отправить по сети или передать его методу, который не понимает Unicode, кодируйте его в str как можно позже.

В этом конкретном случае проблема в том, что вы передаете unicode в warnings.warn () и не можете этого сделать. Передайте строку. Если вы не знаете, что это такое (как, кажется, здесь), потому что оно пришло откуда-то еще, ваши решения try / except с repr работают нормально, хотя выполнение кодирования было бы возможным.

person Lennart Regebro    schedule 06.10.2009
comment
Я думаю, что спрашивающий прекрасно осведомлен о том, что проблема в том, что unicode и str каким-то образом смешаны; вопрос в том, почему эта ошибка возникает при операции, которая обычно должна преобразовывать вывод в Unicode. - person Joakim Lundborg; 06.10.2009
comment
Возможно, но я пошел на исчерпывающий ответ. И проблема по-прежнему заключается в смешивании unicode и str. Почему в данном конкретном случае возникает одна ошибка вместо другой, я не знаю, я не могу ее воспроизвести. Но я сам видел, как это происходило. - person Lennart Regebro; 06.10.2009
comment
Вот как это можно воспроизвести: import warnings; warnings.warn(u'Предупреждение') - person Ivan Virabyan; 14.01.2014

Один из передаваемых операндов не подходит для кодировки ASCII - возможно, он содержит символы Unicode или Latin-1. Измените строку формата на Unicode и посмотрите, что произойдет.

person Vinay Sajip    schedule 06.10.2009
comment
Это должно привести к ошибке декодирования, т.е. s =% s% s% (unichr (2000), chr (200)). Кажется, что ошибка здесь в другом. - person Joakim Lundborg; 06.10.2009
comment
@cortex: Иногда Python решает не принуждать к юникоду, а к строковому. Я не совсем понимаю, как было принято это решение. - person Lennart Regebro; 06.10.2009