Изменить кодировку Python по умолчанию?


146

Когда я запускаю свои приложения с консоли, у меня возникает много проблем с "не могу кодировать" и "не могу декодировать" с Python . Но в Eclipse PyDev IDE кодировка символов по умолчанию установлена ​​на UTF-8 , и я в порядке.

Я искал установку кодировки по умолчанию, и люди говорят, что Python удаляет sys.setdefaultencodingфункцию при запуске, и мы не можем ее использовать.

Итак, какое лучшее решение для этого?


1
См. Сообщение в блоге Призрачное setdefaultencoding .
djc

3
The best solution is to learn to use encode and decode correctly instead of using hacks.Это, безусловно, было возможно с python2 за счет того, что всегда нужно помнить об этом / последовательно использовать свой собственный интерфейс. Мой опыт показывает, что это становится очень проблематичным, когда вы пишете код, который хотите работать как с python2, так и с python3.
Атт Риг

Ответы:


165

Вот более простой метод (взлом), который возвращает вам setdefaultencoding()функцию, которая была удалена из sys:

import sys
# sys.setdefaultencoding() does not exist, here!
reload(sys)  # Reload does the trick!
sys.setdefaultencoding('UTF8')

(Примечание для Python 3.4+: reload()находится в importlibбиблиотеке.)

Однако это небезопасно : очевидно, что это взлом, поскольку sys.setdefaultencoding()он намеренно удаляется sysпри запуске Python. Повторное включение и изменение кодировки по умолчанию может нарушить код, в котором используется ASCII по умолчанию (этот код может быть сторонним, что обычно делает его исправление невозможным или опасным).


5
Я проголосовал против, потому что этот ответ не помогает запускать существующие приложения (что является одним из способов интерпретации вопроса), неверен, когда вы пишете / поддерживаете приложение, и опасен при написании библиотеки. Правильный способ - установить LC_CTYPE(или в приложении проверить, правильно ли он настроен, и прервать работу с появлением значимого сообщения об ошибке).
ibotty

@ibotty Я согласен с тем, что этот ответ является взломом и его опасно использовать. Однако он дает ответ на вопрос («Изменить кодировку Python по умолчанию?»). У вас есть справка о влиянии переменной среды LC_CTYPE на интерпретатор Python?
Эрик О Лебигот

ну не упомянул, сначала это хак. кроме этого, опасные ответы, в которых отсутствует какое-либо упоминание о них, бесполезны.
ibotty

1
@EOL, ты прав. Это действительно влияет на предпочтительное LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
кодирование

1
@ user2394901 Использование sys.setdefaultencoding () всегда не одобрялось !! Кодировка py3k жестко привязана к "utf-8", и ее изменение вызывает ошибку.
Марлон Абейкун

72

Если вы получаете эту ошибку при попытке передать / перенаправить вывод вашего скрипта

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

Просто экспортируйте PYTHONIOENCODING в консоль, а затем запустите свой код.

export PYTHONIOENCODING=utf8


3
Это единственное решение, которое имело для меня значение. - Я использую Debian 7 с неверными настройками локали. Спасибо.
Pryo

4
Установите LC_CTYPEна что - то разумное вместо. Это делает счастливыми и все остальные программы.
ibotty

5
Более серьезная ошибка в Python3 заключается в том, что PYTHONIOENCODING=utf8это не по умолчанию. Это приводит к тому, что сценарии ломаются только потому, чтоLC_ALL=C
Тино

Set LC_CTYPE to something sensible insteadЭто разумное предложение. Это не работает так хорошо, когда вы пытаетесь распространить код, который просто работает в системе другого человека.
Атт Риг

Операционные системы Debian и Redhat используют C.utf8языковой стандарт для обеспечения более разумного C. glibc upstream работает над его добавлением, поэтому, возможно, нам не следует обвинять Python в соблюдении настроек языкового стандарта \…?
Mingye Wang

52

A) Для управления 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) или оба!


(только для python2) отдельно, но интересно, расширяется выше, from __future__ import unicode_literalsсм. обсуждение
lukmdo

18

Начиная с PyDev 3.4.1, кодировка по умолчанию больше не изменяется. Подробности смотрите в этом билете .

Для более ранних версий решение состоит в том, чтобы убедиться, что PyDev не работает с UTF-8 в качестве кодировки по умолчанию. В Eclipse запустите настройки диалога («запустить конфигурации», если я правильно помню); Вы можете выбрать кодировку по умолчанию на общей вкладке. Измените его на US-ASCII, если вы хотите, чтобы эти ошибки возникали «раньше» (другими словами: в вашей среде PyDev). Также см. Исходное сообщение в блоге для этого обходного пути .


1
Спасибо, Крис. Ваш ответ кажется мне наиболее подходящим, особенно с учетом приведенного выше комментария Марка Т. И для человека, который в первую очередь не является пользователем Eclipse / PyDev, я бы никогда не понял этого самостоятельно.
Шон

Я хотел бы изменить это глобально (а не один раз за конфигурацию запуска), но не понял, как - задал отдельный вопрос: stackoverflow.com/questions/9394277/…
Тим Диггинс

13

Что касается 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


8

Об этом есть содержательный пост в блоге.

См. 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.


4

Во-первых: reload(sys)и установка некоторой случайной кодировки по умолчанию только с учетом потребности в потоке выходного терминала - плохая практика. reloadчасто меняет вещи в sys, которые были введены в действие в зависимости от среды, например потоки sys.stdin / stdout, sys.excepthook и т. д.

Решение проблемы с кодированием на stdout

Лучшее решение, которое я знаю для решения проблемы кодирования printстрок unicode и за пределами ascii str(например, из литералов) в sys.stdout, это: позаботиться о sys.stdout (файловом объекте), который способен и необязательно толерантный в отношении потребностей:

  • Когда sys.stdout.encodingэто Noneпо какой - то причине, или несуществующего, или ошибочно ложной или «меньше» , чем стандартный вывод терминала или поток действительно способен, а затем попытаться обеспечить правильный .encodingатрибут. Наконец, путем замены sys.stdout & sys.stderrна переводящий файловый объект.

  • Когда терминал / поток по-прежнему не может кодировать все встречающиеся символы Unicode, и когда вы не хотите прерывать 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. Это можно сделать довольно последовательно (несмотря на то, что говорится в статье anonbadger ), позаботившись об исходном коде Python 2 или Python 2 + 3, который последовательно использует простые строковые литералы ascii или UTF-8 - поскольку эти строки потенциально подвергаются молчанию. преобразование юникода и переход между модулями или, возможно, переход на стандартный вывод. Для этого предпочтите "# 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

Таким образом, строковые литералы и большинство операций (кроме итерации символов) работают комфортно, не думая о преобразовании в Unicode, как если бы был только Python3. Файловый ввод-вывод, конечно, всегда требует особого внимания к кодировкам - как и в Python3.

Примечание: простые строки затем неявно преобразуются из utf-8 в unicode in SmartStdoutперед преобразованием в кодирование выходного потока.


4

Вот подход, который я использовал для создания кода, который был совместим как с python2, так и с python3 и всегда давал вывод utf8 . Я нашел этот ответ в другом месте, но не могу вспомнить источник.

Этот подход работает путем замены sys.stdoutчего-то, что не совсем похоже на файл (но по-прежнему использует только вещи из стандартной библиотеки). Это вполне может вызвать проблемы для ваших базовых библиотек, но в простом случае, когда у вас есть хороший контроль над тем, как sys.stdout используется в вашей структуре, это может быть разумным подходом.

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')

1

Это быстрый способ взлома для всех, кто (1) на платформе Windows (2) работает с Python 2.7 и (3) раздражен тем, что это хорошее программное обеспечение (то есть написанное не вами, поэтому не сразу является кандидатом для кодирования / декодирования печати). maneuvers) не будут отображать «красивые символы Юникода» в среде IDLE (Pythonwin печатает в Юникоде нормально), например, аккуратные символы логики первого порядка, которые Стефан Бойер использует в выводе своего педагогического средства доказательства в программе проверки логики первого порядка .

Мне не понравилась идея принудительной перезагрузки sys, и я не мог заставить систему сотрудничать с настройкой переменных среды, таких как PYTHONIOENCODING (попробовал прямую переменную среды Windows, а также сбросил ее в sitecustomize.py в пакетах сайтов как один liner = '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

Другими словами, закомментируйте исходную строку кода, следующую за ' попыткой ', которая делала переменную кодирования равной locale.getdefaultlocale (потому что это даст вам cp1252, который вам не нужен), и вместо этого переберите его в 'utf-8 '(добавив строку' encoding = 'utf-8 ', как показано).

Я считаю, что это влияет только на отображение IDLE в stdout, а не на кодировку, используемую для имен файлов и т. Д. (Которая получена ранее в файловой системе). Если у вас возникнут проблемы с любым другим кодом, который вы запустите в IDLE позже, просто замените файл IOBinding.py исходным немодифицированным файлом.


1

Это устранило проблему для меня.

import os
os.environ["PYTHONIOENCODING"] = "utf-8"

Не для меня. Но работал, когда экспортировал переменную в оболочку до входа в python, или использовал reload (sys); sys.defaultencoding ("utf-8").
Эрик Х.

У меня тоже не сработало.
MikeB

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.