Как печатать текст с кодировкой UTF-8 на консоли в Python <3?

Я запускаю последнюю систему Linux, где все мои локали UTF-8:

LANG=de_DE.UTF-8
LANGUAGE=
LC_CTYPE="de_DE.UTF-8"
LC_NUMERIC="de_DE.UTF-8"
LC_TIME="de_DE.UTF-8"
...
LC_IDENTIFICATION="de_DE.UTF-8"
LC_ALL=

Теперь я хочу записать кодированный контент UTF-8 на консоль.

Прямо сейчас Python использует UTF-8 для кодировки FS, но придерживается ASCII для кодировки по умолчанию: - (

>>> import sys
>>> sys.getdefaultencoding()
'ascii'
>>> sys.getfilesystemencoding()
'UTF-8'

Я думал, что лучший (чистый) способ сделать это - установить переменную среды PYTHONIOENCODING. Но похоже, что Python игнорирует это. По крайней мере, в моей системе я продолжаю получать ascii как кодировку по умолчанию, даже после установки envvar.

# tried this in ~/.bashrc and ~/.profile (also sourced them)
# and on the commandline before running python
export PYTHONIOENCODING=UTF-8

Если я делаю следующее в начале script, он работает, хотя:

>>> import sys
>>> reload(sys)  # to enable `setdefaultencoding` again
<module 'sys' (built-in)>
>>> sys.setdefaultencoding("UTF-8")
>>> sys.getdefaultencoding()
'UTF-8'

Но этот подход кажется нечистым. Итак, какой хороший способ это сделать?

Обход

Вместо изменения кодировки по умолчанию, которая не является хорошей идеей (см. mesilliac answer) - я просто обертываю sys.stdout с помощью StreamWriter следующим образом:

sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)

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

Ответ 1

Как напечатать текст в кодировке UTF-8 на консоли в Python <3?

print u"some unicode text \N{EURO SIGN}"
print b"some utf-8 encoded bytestring \xe2\x82\xac".decode('utf-8')

то есть, если у вас есть строка Unicode, выведите ее напрямую. Если у вас есть строка байтов, сначала преобразуйте ее в Unicode.

В ваших настройках локали (LANG, LC_CTYPE) указывается локаль utf-8, и поэтому (теоретически) вы можете распечатать строку тестирования utf-8 напрямую, и она должна правильно отображаться в вашем терминале (если настройки терминала соответствуют настройкам локали и они должно быть) но вы должны избегать этого: не жестко кодируйте кодировку символов вашей среды внутри скрипта; вместо этого выведите Unicode напрямую.

В вашем вопросе много неверных предположений.

Вам не нужно устанавливать PYTHONIOENCODING с вашими настройками локали, чтобы распечатать Unicode на терминале. Язык UTF-8 поддерживает все символы Unicode, т.е. работает как есть.

Вам не нужно обходное решение sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout). Он может сломаться, если какой-то код (который вы не контролируете) действительно должен напечатать байты, и/или он может сломаться при печати Unicode на консоль Windows (неправильная кодовая страница, не может печатать некодируемые символы). Достаточно правильных настроек локали и/или PYTHONIOENCODING envvar. Также, если вам нужно заменить sys.stdout тогда используйте io.TextIOWrapper() вместо модуля codecs как это делает пакет win-unicode-console.

sys.getdefaultencoding() не связан с вашими настройками локали и PYTHONIOENCODING. Ваше предположение, что настройка PYTHONIOENCODING должна изменить sys.getdefaultencoding(), неверно. sys.stdout.encoding этого вы должны проверить sys.stdout.encoding.

sys.getdefaultencoding() не используется при печати на консоли. Он может использоваться как запасной вариант в Python 2, если stdout перенаправляется в файл/канал, если не установлено PYTHOHIOENCODING:

$ python2 -c'import sys; print(sys.stdout.encoding)'
UTF-8
$ python2 -c'import sys; print(sys.stdout.encoding)' | cat
None
$ PYTHONIOENCODING=utf8 python2 -c'import sys; print(sys.stdout.encoding)' | cat
utf8

Не вызывайте sys.setdefaultencoding("UTF-8"); это может молча повредить ваши данные и/или сломать сторонние модули, которые этого не ожидают. Помните, что sys.getdefaultencoding() используется для неявного преобразования байтовых строк (str) в/из unicode в Python 2, например, "a" + u"b". Смотрите также цитату в ответе @mesilliac.

Ответ 2

Кажется, что выполнение этого не рекомендуется.

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

Здесь приводится цитата из рассылка списка рассылки:

The only supported default encodings in Python are:

 Python 2.x: ASCII
 Python 3.x: UTF-8

If you change these, you are on your own and strange things will
start to happen. The default encoding does not only affect
the translation between Python and the outside world, but also
all internal conversions between 8-bit strings and Unicode.

Hacks like what happening in the pango module (setting the
default encoding to 'utf-8' by reloading the site module in
order to get the sys.setdefaultencoding() API back) are just
downright wrong and will cause serious problems since Unicode
objects cache their default encoded representation.

Please don't enable the use of a locale based default encoding.

If all you want to achieve is getting the encodings of
stdout and stdin correctly setup for pipes, you should
instead change the .encoding attribute of those (only).

-- 
Marc-Andre Lemburg
eGenix.com

Ответ 3

Вот как я это делаю:

#!/usr/bin/python2.7 -S

import sys
sys.setdefaultencoding("utf-8")
import site

Обратите внимание на -S в bangline. Это говорит Python не автоматически импортировать модуль site. Модуль site - это то, что устанавливает кодировку по умолчанию, и удаляет метод, чтобы он не мог быть установлен снова. Но будет чтить то, что уже установлено.

Ответ 4

Если программа не отображает соответствующие символы на экране, т.е. недопустимый символ, запустите программу с помощью следующей командной строки:

PYTHONIOENCODING=utf8 python3 yourprogram.py

Или следующее, если ваша программа является глобально установленным модулем:

PYTHONIOENCODING=utf8 yourprogram

На некоторых платформах, таких как Cygwin (терминал mintty.exe) с Anaconda Python (или Python 3), просто запустите export PYTHONIOENCODING=utf8 а затем запуск программы не работает, и вы должны всегда выполнять каждый раз, когда PYTHONIOENCODING=utf8 yourprogram для запуска программа правильно.

В Linux, в случае sudo, вы можете попытаться передать аргумент -E для экспорта пользовательских переменных в процесс sudo:

export PYTHONIOENCODING=utf8
sudo -E python yourprogram.py

Если вы попробуете это, и это не сработало, вам нужно будет войти в оболочку sudo:

sudo /bin/bash
PYTHONIOENCODING=utf8 yourprogram

Связанные с:

  1. Как напечатать текст в кодировке UTF-8 на консоли в Python <3?
  2. Изменить кодировку Python по умолчанию?
  3. Форсирование UTF-8 через cp1252 (Python3)
  4. Постоянно установить путь Python для Анаконды в Cygwin
  5. https://superuser.com/info/1374339/what-does-the-E-in-sudo-E-do
  6. Почему bash -c 'var = 5 printf "$ var"' не печатает 5?
  7. https://unix.stackexchange.com/info/296838/whats-the-difference-between-Eval-and-Exec