Ответ 1
Мне непонятно, почему вы не сможете печатать; но, полагая, да, подход выглядит для меня правильным.
Предположим на мгновение, что нельзя использовать print
(и, таким образом, воспользоваться возможностью автоматического обнаружения кодирования). Итак, это оставляет нас с sys.stdout
. Тем не менее, sys.stdout
настолько тупо, что не делает никакого разумного кодирования.
Теперь читаем страницу вики Python PrintFails и отправляем попробовать следующий код:
$ python -c 'import sys, codecs, locale; print str(sys.stdout.encoding); \
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout);
Однако это тоже не работает (по крайней мере, на Mac). Слишком понятно, почему:
>>> import locale
>>> locale.getpreferredencoding()
'mac-roman'
>>> sys.stdout.encoding
'UTF-8'
(UTF-8 - это то, что понимает один терминал).
Таким образом, один из приведенных выше вариантов кода:
$ python -c 'import sys, codecs, locale; print str(sys.stdout.encoding); \
sys.stdout = codecs.getwriter(sys.stdout.encoding)(sys.stdout);
И теперь строки unicode отправляются правильно на sys.stdout
и, следовательно, правильно печатаются на терминале (sys.stdout
подключен терминал).
Является ли это правильным способом записи строк в unicode в sys.stdout
или я должен делать что-то еще?
РЕДАКТИРОВАТЬ: время от времени, скажем, при прокладке на выходе less
- sys.stdout.encoding
будет None
. в этом случае вышеуказанный код не будет выполнен.
Мне непонятно, почему вы не сможете печатать; но, полагая, да, подход выглядит для меня правильным.
export PYTHONIOENCODING=utf-8
выполнит задание, но не сможет установить его на самом python...
что мы можем сделать, это проверить, не задано ли значение и сообщить пользователю установить его перед вызовом script с помощью
if __name__ == '__main__':
if (sys.stdout.encoding is None):
print >> sys.stderr, "please set python env PYTHONIOENCODING=UTF-8, example: export PYTHONIOENCODING=UTF-8, when write to stdout."
exit(1)
Лучше всего проверить, подключены ли вы напрямую к терминалу. Если да, используйте терминальную кодировку. В противном случае используйте предпочтительную кодировку системы.
if sys.stdout.isatty():
default_encoding = sys.stdout.encoding
else:
default_encoding = locale.getpreferredencoding()
Также очень важно всегда указывать пользователю какую кодировку она хочет. Обычно я делаю это командной строкой (например, -e ENCODING
) и анализирую ее с помощью модуля optparse
.
Еще одна хорошая вещь - не перезаписать sys.stdout
с помощью автоматического кодера. Создайте свой кодер и используйте его, но оставьте sys.stdout
в покое. Вы можете импортировать сторонние библиотеки, которые записывают закодированные байты непосредственно в sys.stdout
.
Существует необязательная переменная среды "PYTHONIOENCODING", которая может быть установлена на желаемую кодировку по умолчанию. Это был бы один из способов захвата желаемой пользователем кодировки способом, совместимым со всем Python. Он похоронен в руководстве Python здесь.
Это то, что я делаю в своем приложении:
sys.stdout.write(s.encode('utf-8'))
Это точное противоположное исправление для чтения имен UTF-8 из argv:
for file in sys.argv[1:]:
file = file.decode('utf-8')
Это очень уродливо (IMHO), поскольку это заставляет вас работать с UTF-8.., что является нормой на Linux/Mac, но не на окнах... Работает для меня в любом случае:)