Изменить кодировку Python по умолчанию?
У меня много проблем "не могу кодировать" и "не могу декодировать" с Python, когда я запускаю свои приложения из консоли. Но в Eclipse PyDev IDE кодировка символов по умолчанию установлена в UTF-8, и я в порядке.
Я искал для установки кодировки по умолчанию, и люди говорят, что Python удаляет sys.setdefaultencoding
функция при запуске, и мы не можем ее использовать.
Так что же является лучшим решением для этого?
15 ответов
Вот более простой метод (хак), который возвращает вам setdefaultencoding()
функция, которая была удалена из sys
:
import sys
# sys.setdefaultencoding() does not exist, here!
reload(sys) # Reload does the trick!
sys.setdefaultencoding('UTF8')
Однако это небезопасно: очевидно, это взлом, поскольку sys.setdefaultencoding()
намеренно удален из sys
когда Python запускается. Повторное включение и изменение кодировки по умолчанию может привести к поломке кода, который по умолчанию использует ASCII (этот код может быть сторонним, что обычно делает его невозможным или опасным).
Если вы получаете эту ошибку при попытке перенаправить / перенаправить вывод вашего скрипта
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
Просто экспортируйте PYTHONIOENCODING в консоль, а затем запустите свой код.
export PYTHONIOENCODING=utf8
А) Для контроля sys.getdefaultencoding()
выход:
python -c 'import sys; print(sys.getdefaultencoding())'
ascii
затем
echo "import sys; sys.setdefaultencoding('utf-16-be')" > sitecustomize.py
а также
PYTHONPATH=".:$PYTHONPATH" python -c 'import sys; print(sys.getdefaultencoding())'
utf-16-be
Вы можете поместить ваш sitecustomize.py выше в вашем PYTHONPATH
,
Также вы можете попробовать reload(sys).setdefaultencoding
@EOL
Б) контролировать stdin.encoding
а также stdout.encoding
Вы хотите установить PYTHONIOENCODING
:
python -c 'import sys; print(sys.stdin.encoding, sys.stdout.encoding)'
ascii ascii
затем
PYTHONIOENCODING="utf-16-be" python -c 'import sys;
print(sys.stdin.encoding, sys.stdout.encoding)'
utf-16-be utf-16-be
Наконец: вы можете использовать A) или B) или оба!
Начиная с PyDev 3.4.1, кодировка по умолчанию больше не изменяется. Смотрите этот билет для деталей.
Для более ранних версий решение состоит в том, чтобы убедиться, что PyDev не работает с UTF-8 в качестве кодировки по умолчанию. Под Eclipse запустите настройки диалога ("запустите настройки", если я правильно помню); Вы можете выбрать кодировку по умолчанию на общей вкладке. Измените его на US-ASCII, если вы хотите, чтобы эти ошибки были "ранними" (другими словами: в вашей среде PyDev). Также см. Оригинальный пост в блоге для этого обходного пути.
Что касается python2 (и только python2), некоторые из предыдущих ответов основаны на использовании следующего хака:
import sys
reload(sys) # Reload is a hack
sys.setdefaultencoding('UTF8')
Не рекомендуется использовать его (проверьте это или это)
В моем случае это имеет побочный эффект: я использую ноутбуки ipython, и как только я запускаю код, функция "print" больше не работает. Я думаю, что было бы решение, но все же я думаю, что использование взлома не должно быть правильным вариантом.
Попробовав много вариантов, тот, который работал для меня, использовал тот же код в sitecustomize.py
где этот кусок кода должен быть. После оценки этого модуля функция setdefaultencoding удаляется из sys.
Таким образом, решение состоит в том, чтобы добавить в файл /usr/lib/python2.7/sitecustomize.py
код:
import sys
sys.setdefaultencoding('UTF8')
Когда я использую virtualenvwrapper, файл, который я редактирую, ~/.virtualenvs/venv-name/lib/python2.7/sitecustomize.py
,
И когда я использую с ноутбуками Python и Conda, это ~/anaconda2/lib/python2.7/sitecustomize.py
Об этом есть проницательный пост в блоге.
См. https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/.
Я перефразирую его содержание ниже.
В Python 2, который не был так строго типизирован в отношении кодирования строк, вы могли выполнять операции над строками с различным кодированием и добиваться успеха. Например, следующее вернется True
,
u'Toshio' == 'Toshio'
Это будет выполняться для каждой (нормальной, без префикса) строки, которая была закодирована в sys.getdefaultencoding()
, который по умолчанию ascii
, но не другие.
Кодировка по умолчанию должна была быть изменена во всей системе в site.py
, но не где-то еще. Хаки (также представленные здесь), чтобы установить его в пользовательских модулях, были просто: хаки, а не решение.
Python 3 изменил кодировку системы по умолчанию на utf-8 (когда LC_CTYPE поддерживает Unicode), но фундаментальная проблема была решена с требованием явно кодировать "байтовые" строки всякий раз, когда они используются со строками Unicode.
Вот подход, который я использовал для создания кода, который был совместим как с python2, так и с python3 и всегда создавал вывод utf8. Я нашел этот ответ в другом месте, но я не могу вспомнить источник.
Этот подход работает путем замены sys.stdout
с чем-то, что не совсем похоже на файл (но все еще использует вещи в стандартной библиотеке). Это может вызвать проблемы для ваших базовых библиотек, но в простом случае, когда вы имеете хороший контроль над тем, как sys.stdout out используется в вашей среде, это может быть разумным подходом.
sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
Первый: reload(sys)
и установка некоторой случайной кодировки по умолчанию только с учетом необходимости потока выходного терминала является плохой практикой. reload
часто меняет в sys вещи, которые были установлены в зависимости от среды - например, потоки sys.stdin /stdout, sys.excepthook и т. д.
Решение проблемы кодирования на stdout
Лучшее решение, которое я знаю для решения проблемы кодирования print
'Unicode-строки и выход за пределы ASCII str
(например, из литералов) в sys.stdout: позаботиться о sys.stdout (объектоподобном объекте), который способен и необязательно переносит требования:
когда
sys.stdout.encoding
являетсяNone
по какой-то причине, либо не существует, либо ошибочно ложно или "меньше", чем то, на что действительно способен терминал или поток stdout, затем попытайтесь предоставить правильное.encoding
приписывать. Наконец, заменивsys.stdout & sys.stderr
с помощью переводящего файлового объекта.Когда терминал / поток все еще не может кодировать все встречающиеся символы Юникода, и когда вы не хотите прерывать
print
Именно поэтому вы можете ввести поведение кодирования с заменой в переводимом файловом объекте.
Вот пример:
#!/usr/bin/env python
# encoding: utf-8
import sys
class SmartStdout:
def __init__(self, encoding=None, org_stdout=None):
if org_stdout is None:
org_stdout = getattr(sys.stdout, 'org_stdout', sys.stdout)
self.org_stdout = org_stdout
self.encoding = encoding or \
getattr(org_stdout, 'encoding', None) or 'utf-8'
def write(self, s):
self.org_stdout.write(s.encode(self.encoding, 'backslashreplace'))
def __getattr__(self, name):
return getattr(self.org_stdout, name)
if __name__ == '__main__':
if sys.stdout.isatty():
sys.stdout = sys.stderr = SmartStdout()
us = u'aouäöüфżß²'
print us
sys.stdout.flush()
Использование простых строк за пределами ascii в коде Python 2 / 2 + 3
Я думаю, что единственная веская причина изменить глобальную кодировку по умолчанию (только на UTF-8) касается решения об исходном коде приложения, а не из-за проблем с кодировкой потока ввода-вывода: для записи строковых литералов за пределами ascii в код без принудительного применения всегда использовать u'string'
Стиль Unicode Escape. Это может быть сделано довольно последовательно (несмотря на то, что говорится в статье anonbadger), позаботившись о основе исходного кода Python 2 или Python 2 + 3, которая последовательно использует литералы простых строк ascii или UTF-8 - насколько эти строки потенциально подвергаются молчанию Unicode преобразование и перемещение между модулями или, возможно, перейти на стандартный вывод. За что предпочитаю # encoding: utf-8
"или ascii (без декларации). Измените или удалите библиотеки, которые все еще очень тупо полагаются на ошибки кодирования по умолчанию ascii после chr #127 (что сегодня встречается редко).
И делать это при запуске приложения (и / или через sitecustomize.py) в дополнение к SmartStdout
схема выше - без использования reload(sys)
:
...
def set_defaultencoding_globally(encoding='utf-8'):
assert sys.getdefaultencoding() in ('ascii', 'mbcs', encoding)
import imp
_sys_org = imp.load_dynamic('_sys_org', 'sys')
_sys_org.setdefaultencoding(encoding)
if __name__ == '__main__':
sys.stdout = sys.stderr = SmartStdout()
set_defaultencoding_globally('utf-8')
s = 'aouäöüфżß²'
print s
Таким образом, строковые литералы и большинство операций (кроме итерации символов) работают комфортно, не задумываясь о преобразовании в юникод, как если бы существовал только Python3. Разумеется, файловый ввод / вывод всегда требует особой осторожности в отношении кодировок, как это делается в Python3.
Примечание: простые строки затем неявно преобразуются из UTF-8 в Unicode в SmartStdout
до преобразования в конечный поток вывода.
Это быстрый взлом для любого, кто (1) на платформе Windows (2) работает под управлением Python 2.7 и (3) раздражен, потому что хороший кусок программного обеспечения (т. Е. Не написан вами, поэтому не является сразу кандидатом для кодирования / декодирования печати) маневры) не будут отображать "симпатичные символы юникода" в среде IDLE (Pythonwin печатает отлично с юникодом), например, аккуратные символы логики первого порядка, которые Стефан Бойер использует в выходных данных своего педагогического испытателя в тесте логики первого порядка.
Мне не нравилась идея принудительной перезагрузки системы, и я не мог заставить систему взаимодействовать с установкой переменных среды, таких как PYTHONIOENCODING (пробовал прямую переменную среды Windows, а также сбрасывал ее в sitecustomize.py в пакетах сайта как единое целое. лайнер ='utf-8').
Итак, если вы хотите взломать свой путь к успеху, перейдите в каталог IDLE, как правило: "C:\Python27\Lib\idlelib". Найдите файл IOBinding.py. Сделайте копию этого файла и сохраните ее где-нибудь еще, чтобы вы могли вернуться к исходному поведению, когда захотите. Откройте файл в idlelib с помощью редактора (например, IDLE). Перейти к этой области кода:
# Encoding for file names
filesystemencoding = sys.getfilesystemencoding()
encoding = "ascii"
if sys.platform == 'win32':
# On Windows, we could use "mbcs". However, to give the user
# a portable encoding name, we need to find the code page
try:
# --> 6/5/17 hack to force IDLE to display utf-8 rather than cp1252
# --> encoding = locale.getdefaultlocale()[1]
encoding = 'utf-8'
codecs.lookup(encoding)
except LookupError:
pass
Другими словами, закомментируйте оригинальную строку кода после 'try', который делал переменную кодирования равной locale.getdefaultlocale (потому что это даст вам cp1252, который вам не нужен), и вместо этого просто переберите его в 'utf-8' (добавив строку'encoding = 'utf-8', как показано).
Я полагаю, что это влияет только на отображение IDLE на стандартный вывод, а не на кодировку, используемую для имен файлов и т. Д. (Что было получено ранее в файловой системе кодирования). Если у вас есть проблема с любым другим кодом, который вы запускаете в IDLE позже, просто замените файл IOBinding.py на исходный неизмененный файл.
Это исправило проблему для меня.
import os
os.environ["PYTHONIOENCODING"] = "utf-8"
Вы можете изменить кодировку всей вашей операционной системы. В Ubuntu это можно сделать с помощью
sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo dpkg-reconfigure locales
Если вам нужна только стабильная
UTF-8
поддержка в файле
read
/
write
без одинаковых объявлений везде, вот два решения:
1. Патч
io
модуль во время выполнения (опасная операция на свой страх и риск)
import pathlib as pathlib
import tempfile
import chardet
def patchIOWithUtf8Default():
import builtins
import importlib.util
import sys
spec = importlib.util.find_spec("io")
module = importlib.util.module_from_spec(spec)
exec(compile(spec.loader.get_source(spec.name) + """
def open(*args, **kwargs):
args = list(args)
mode = kwargs.get('mode', (args + [''])[1])
if (len(args) < 4 and 'b' not in mode) or 'encoding' in kwargs:
kwargs['encoding'] = 'utf8'
elif len(args) >= 4 and args[3] is None:
args[3] = 'utf8'
return _io.open(*args, **kwargs)
""", module.__spec__.origin, "exec"), module.__dict__)
sys.modules[module.__name__] = module
builtins.open = __import__("io").open
importlib.reload(importlib.import_module("pathlib"))
def main():
patchIOWithUtf8Default()
filename = tempfile.mktemp()
text = "Common\n常\nSense\n识\n天地玄黄"
print("Original text:", repr(text))
pathlib.Path(filename).write_text(text)
encoding = chardet.detect(open(filename, mode="rb").read())["encoding"]
print("Written encoding by pathlib:", encoding)
print("Written text by pathlib:", repr(open(filename, newline="", encoding=encoding).read()))
if __name__ == '__main__':
main()
Пример вывода:
Original text: 'Common\n常\nSense\n识\n天地玄黄'
Written encoding by pathlib: utf-8
Written text by pathlib: 'Common\r\n常\r\nSense\r\n识\r\n天地玄黄'
2. Используйте третью библиотеку в качестве оболочки pathlib
https://github.com/baijifeilong/IceSpringPathLib
pip install IceSpringPathLib
import pathlib
import tempfile
import chardet
import IceSpringPathLib
tempfile.mktemp()
filename = tempfile.mktemp()
text = "Common\n常\nSense\n识\n天地玄黄"
print("Original text:", repr(text))
pathlib.Path(filename).write_text(text)
encoding = chardet.detect(open(filename, mode="rb").read())["encoding"]
print("\nWritten text by pathlib:", repr(open(filename, newline="", encoding=encoding).read()))
print("Written encoding by pathlib:", encoding)
IceSpringPathLib.Path(filename).write_text(text)
encoding = chardet.detect(open(filename, mode="rb").read())["encoding"]
print("\nWritten text by IceSpringPathLib:", repr(open(filename, newline="", encoding=encoding).read()))
print("Written encoding by IceSpringPathLib:", encoding)
Пример вывода:
Original text: 'Common\n常\nSense\n识\n天地玄黄'
Written text by pathlib: 'Common\r\n常\r\nSense\r\n识\r\n天地玄黄'
Written encoding by pathlib: GB2312
Written text by IceSpringPathLib: 'Common\n常\nSense\n识\n天地玄黄'
Written encoding by IceSpringPathLib: utf-8
установить кодировку ОС по умолчанию
UTF-8
. Например, в файле редактирования ubuntu
/etc/default/locale
и установите:
LANG=en_US.UTF-8
LANGUAGE=en_US.UTF-8
LC_ALL=en_US.UTF-8
Вы хотите написать испанские слова (para escribir la - en python)
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
print "Piña"