Зміна кодування Python за замовчуванням?


143

У мене багато проблем "не можна кодувати" та "не можу розшифрувати" з Python, коли я запускаю свої програми з консолі. Але в IDE Eclipse PyDev для кодування символів за замовчуванням встановлено UTF-8 , і я добре.

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

То яке найкраще рішення для цього?


1
Дивіться публікацію в щоденнику блогу Ілюзорне встановлення коду .
djc

3
The best solution is to learn to use encode and decode correctly instead of using hacks.Це, безумовно, було можливо з python2 ціною завжди пам'ятати, щоб зробити це / послідовно використовувати власний інтерфейс. Мій досвід говорить про те, що це стає дуже проблематичним, коли ви пишете код, який потрібно працювати як з python2, так і з python3.
Att Righ

Відповіді:


159

Ось простіший метод (хак), який повертає 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", а зміна його спричиняє помилку.
Марлон Абейкон

70

Якщо ви отримаєте цю помилку, коли ви намагаєтеся передати / перенаправити вихід свого сценарію

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

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

export PYTHONIOENCODING=utf8


3
Це єдине рішення, яке мало для мене значення. - Я на Debian 7, зі зламаними налаштуваннями мови. Дякую.
Пріо

4
Налаштуйте LC_CTYPEнатомість щось розумне. Це робить і всі інші програми щасливими.
ibotty

5
Більша помилка в Python3 - PYTHONIOENCODING=utf8це не за замовчуванням. Це робить сценарії ламаними лише тому, щоLC_ALL=C
Тіно,

Set LC_CTYPE to something sensible insteadЦе розумна пропозиція. Це не працює настільки добре, коли ви намагаєтесь поширити код, який просто працює в системі іншої людини.
Att Righ

ОС Debian і Redhat використовують C.utf8локаль, щоб забезпечити більш розумний C. glibc вгору за течією працює над його додаванням, тож, можливо, ми не повинні звинувачувати Python у дотриманні налаштувань локалі \ ...?
Arthur2e5

52

А) Для керування 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

B) Щоб керувати 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

17

Починаючи з PyDev 3.4.1, кодування за замовчуванням більше не змінюється. Детальніше дивіться у цьому квитку .

Для більш ранніх версій рішення - переконатися, що PyDev не працює з UTF-8 як кодування за замовчуванням. У розділі Eclipse запустіть налаштування діалогу ("запустити конфігурації", якщо я правильно пам'ятаю); ви можете вибрати кодування за замовчуванням на загальній вкладці. Змініть його на US-ASCII, якщо ви хочете мати ці помилки "рано" (іншими словами: у вашому середовищі PyDev). Також дивіться оригінальну публікацію в блозі для цього вирішення .


1
Дякую Крису. Особливо з огляду на коментар Марка Т вище, ваша відповідь здається мені найбільш прийнятною. А для когось, хто в першу чергу не є користувачем Eclipse / PyDev, я ніколи не зрозумів би це самостійно.
Шон

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

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'ing-рядків unicode і поза-ascii str' s (наприклад, від літералів) на 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 послідовно - наскільки ці рядки потенційно проходять беззвучно перетворення unicode та переміщення між модулями або потенційно перейти до stdout. Для цього віддайте перевагу "# 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перш ніж перетворюються на вихідний потік.


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), дратується, тому що приємний фрагмент програмного забезпечення (тобто, не написаний вами, так що не одразу є кандидатом на кодування / декодування друку маневри) не відображатимуть «симпатичних унікодних символів» у середовищі IDLE (Pythonwin друкує штраф унікоду), наприклад, акуратні логічні символи першого порядку, які використовує Стефан Бойєр у висновку свого педагогічного доказу при логічному доказі першого порядку .

Мені не сподобалася ідея змушувати перезавантажувати sys, і мені не вдалося змусити систему співпрацювати з налаштуванням змінних оточуючих середовищ, таких як PYTHONIOENCODING (спробувала пряму змінну середовища Windows, а також скинувши її в sitecustomize.py в site-пакети як єдиний liner = 'utf-8').

Отже, якщо ви готові зламати шлях до успіху, перейдіть до каталогу IDLE, як правило: "C: \ Python27 \ Lib \ idlelib" Знайдіть файл IOBinding.py. Створіть копію цього файлу і зберігайте його десь в іншому місці, щоб ви могли повернутися до оригінальної поведінки, коли захочете. Відкрийте файл у режимі очікування за допомогою редактора (наприклад, 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 оригінальним немодифікованим файлом.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.