Zmieniasz domyślne kodowanie Pythona?

143

Mam wiele problemów „nie mogę zakodować” i „nie mogę dekodować” w Pythonie, kiedy uruchamiam swoje aplikacje z konsoli. Ale w Eclipse PyDev IDE domyślne kodowanie znaków jest ustawione na UTF-8 i wszystko w porządku.

Szukałem w pobliżu domyślnego kodowania, a ludzie mówią, że Python usuwa sys.setdefaultencodingfunkcję przy starcie i nie możemy jej używać.

Więc jakie jest na to najlepsze rozwiązanie?

Ali Nadalizadeh
źródło
1
Zobacz wpis na blogu The Illusive setdefaultencoding .
djc
3
The best solution is to learn to use encode and decode correctly instead of using hacks.Było to z pewnością możliwe dzięki python2 kosztem ciągłego pamiętania o tym / konsekwentnego używania własnego interfejsu. Z mojego doświadczenia wynika, że ​​staje się to bardzo problematyczne, gdy piszesz kod, z którym chcesz pracować zarówno z python2, jak i python3.
Att Righ

Odpowiedzi:

159

Oto prostsza metoda (hack), która zwraca setdefaultencoding()funkcję, która została usunięta z sys:

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

(Uwaga dla Pythona 3.4+: reload()jest w importlibbibliotece.)

Nie jest to jednak bezpieczna rzecz : jest to oczywiście hack, ponieważ sys.setdefaultencoding()jest celowo usuwany syspodczas uruchamiania Pythona. Ponowne włączenie go i zmiana domyślnego kodowania może zepsuć kod, który opiera się na domyślnym ASCII (ten kod może być innej firmy, co generalnie uniemożliwiłoby lub byłoby niebezpieczne).

Eric O Lebigot
źródło
5
Głosowałem negatywnie, ponieważ ta odpowiedź nie pomaga w uruchamianiu istniejących aplikacji (co jest jednym ze sposobów interpretacji pytania), jest błędna podczas pisania / utrzymywania aplikacji i niebezpieczna podczas pisania biblioteki. Właściwy sposób to ustawić LC_CTYPE(lub w aplikacji sprawdzić, czy jest ustawiony prawidłowo i przerwać, wyświetlając znaczący komunikat o błędzie).
ibotty
@ibotty Zgadzam się, że ta odpowiedź to włamanie i używanie jej jest niebezpieczne. Odpowiada jednak na pytanie („Zmiana domyślnego kodowania w Pythonie?”). Czy masz odniesienie do wpływu zmiennej środowiskowej LC_CTYPE na interpreter Pythona?
Eric O Lebigot
cóż, nie wspomniał, to na początku hack. poza tym niebezpieczne odpowiedzi, w których nie ma żadnej wzmianki, że są, nie są pomocne.
ibotty
1
@EOL masz rację. Ma to jednak wpływ na preferowane LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
kodowanie
1
@ user2394901 Używanie sys.setdefaultencoding () zawsze było odradzane !! A kodowanie py3k jest na stałe połączone z „utf-8” i jego zmiana powoduje błąd.
Marlon Abeykoon
70

Jeśli pojawi się ten błąd podczas próby przesłania / przekierowania wyjścia skryptu

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

Po prostu wyeksportuj PYTHONIOENCODING w konsoli, a następnie uruchom kod.

export PYTHONIOENCODING=utf8

iman
źródło
3
To jedyne rozwiązanie, które zrobiło dla mnie jakąkolwiek różnicę. - Jestem na Debianie 7, z uszkodzonymi ustawieniami regionalnymi. Dzięki.
Pryo,
4
LC_CTYPEZamiast tego wybierz coś rozsądnego. To sprawia, że ​​wszystkie inne programy są również szczęśliwe.
ibotty
5
Większy błąd w Pythonie3 polega na tym, że PYTHONIOENCODING=utf8nie jest to błąd domyślny. To sprawia, że ​​skrypty psują się tylko dlatego, żeLC_ALL=C
Tino
Set LC_CTYPE to something sensible insteadTo rozsądna sugestia. To nie działa tak dobrze, gdy próbujesz rozpowszechniać kod, który po prostu działa w systemie innej osoby.
Att Righ
Systemy operacyjne Debian i Redhat używają C.utf8locale, aby zapewnić bardziej rozsądne ustawienia regionalne C. glibc pracuje nad dodaniem go, więc może nie powinniśmy winić Pythona za przestrzeganie ustawień regionalnych \…?
Arthur2e5
52

A) Aby sterować sys.getdefaultencoding()wyjściem:

python -c 'import sys; print(sys.getdefaultencoding())'

ascii

Następnie

echo "import sys; sys.setdefaultencoding('utf-16-be')" > sitecustomize.py

i

PYTHONPATH=".:$PYTHONPATH" python -c 'import sys; print(sys.getdefaultencoding())'

utf-16-be

Możesz umieścić plik sitecustomize.py wyżej w swoim PYTHONPATH.

Możesz także spróbować reload(sys).setdefaultencodingprzez @EOL

B) Aby kontrolować stdin.encodingi stdout.encodingchcesz ustawić PYTHONIOENCODING:

python -c 'import sys; print(sys.stdin.encoding, sys.stdout.encoding)'

ascii ascii

Następnie

PYTHONIOENCODING="utf-16-be" python -c 'import sys; 
print(sys.stdin.encoding, sys.stdout.encoding)'

utf-16-be utf-16-be

Wreszcie: możesz użyć A) lub B) lub obu!

lukmdo
źródło
(tylko python2) oddzielny, ale interesujący jest rozszerzony powyżej, from __future__ import unicode_literalspatrz dyskusja
lukmdo
17

Począwszy od PyDev 3.4.1, domyślne kodowanie nie jest już zmieniane. Zobacz ten bilet, aby uzyskać szczegółowe informacje.

W przypadku wcześniejszych wersji rozwiązaniem jest upewnienie się, że PyDev nie działa z kodowaniem UTF-8 jako domyślnym. Pod Eclipse uruchom ustawienia okna dialogowego („uruchom konfiguracje”, jeśli dobrze pamiętam); możesz wybrać domyślne kodowanie na wspólnej karcie. Zmień to na US-ASCII, jeśli chcesz mieć te błędy „wcześnie” (innymi słowy: w środowisku PyDev). Zobacz także oryginalny wpis na blogu dotyczący tego obejścia .

ChristopheD
źródło
1
Dzięki Chris. Zwłaszcza biorąc pod uwagę powyższy komentarz Marka T. Twoja odpowiedź wydaje mi się najbardziej odpowiednia. A dla kogoś, kto nie jest przede wszystkim użytkownikiem Eclipse / PyDev, nigdy bym tego nie wymyślił.
Sean
Chciałbym to zmienić globalnie (zamiast konfiguracji raz na przebieg), ale nie wymyśliłem, jak to zrobić - poprosiłem o oddzielne pytanie: stackoverflow.com/questions/9394277/ ...
Tim Diggins,
13

Jeśli chodzi o python2 (i tylko python2), niektóre z poprzednich odpowiedzi polegają na użyciu następującego hackowania:

import sys
reload(sys)  # Reload is a hack
sys.setdefaultencoding('UTF8')

Odradza się go używać (sprawdź to lub to )

W moim przypadku ma to efekt uboczny: używam notebooków ipython i po uruchomieniu kodu funkcja „print” przestaje działać. Myślę, że byłoby rozwiązanie tego problemu, ale nadal uważam, że użycie hacka nie powinno być właściwą opcją.

Po wypróbowaniu wielu opcji, ta, która działała dla mnie, polegała na użyciu tego samego kodu w miejscu sitecustomize.py, w którym powinien znajdować się ten fragment kodu . Po ocenie tego modułu funkcja setdefaultencoding jest usuwana z sys.

Więc rozwiązaniem jest dołączenie do pliku /usr/lib/python2.7/sitecustomize.pykodu:

import sys
sys.setdefaultencoding('UTF8')

Kiedy używam virtualenvwrapper, edytowany przeze mnie plik to ~/.virtualenvs/venv-name/lib/python2.7/sitecustomize.py.

A kiedy używam z notatnikami Pythona i conda, tak jest ~/anaconda2/lib/python2.7/sitecustomize.py

kiril
źródło
8

Jest na ten temat wnikliwy post na blogu.

Zobacz https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/ .

Poniżej parafrazuję jego treść.

W Pythonie 2, który nie był tak silnie wpisany pod względem kodowania ciągów, można było wykonywać operacje na inaczej zakodowanych łańcuchach i odnieść sukces. Np True. Wrócą następujące .

u'Toshio' == 'Toshio'

To będzie obowiązywać dla każdego (normalnego, bez prefiksu) ciągu, który został zakodowany sys.getdefaultencoding(), który domyślnie był ustawiony na ascii, ale nie dla innych.

Domyślne kodowanie miało zostać zmienione w całym systemie site.py, ale nie gdzie indziej. Hacki (również tutaj przedstawione), aby ustawić go w modułach użytkownika, były po prostu: hacki, a nie rozwiązanie.

Python 3 zmienił kodowanie systemu na domyślne na utf-8 (gdy LC_CTYPE obsługuje Unicode), ale podstawowy problem został rozwiązany z wymogiem jawnego kodowania łańcuchów „bajtowych”, ilekroć są one używane z ciągami znaków Unicode.

ibotty
źródło
4

Po pierwsze: reload(sys)a ustawienie jakiegoś losowego domyślnego kodowania tylko ze względu na potrzebę strumienia terminala wyjściowego jest złą praktyką. reloadczęsto zmienia rzeczy w sys, które zostały wprowadzone w zależności od środowiska - np. strumienie sys.stdin / stdout, sys.excepthook itp.

Rozwiązanie problemu z kodowaniem na stdout

Najlepszym rozwiązaniem, jakie znam, aby rozwiązać problem z kodowaniem w przypadku printciągów znaków Unicode i poza-ascii str(np. Z literałów) na sys.stdout jest: zająć się sys.stdout (obiekt podobny do pliku), który jest zdolny i opcjonalnie tolerancyjny w stosunku do potrzeb:

  • Kiedy sys.stdout.encodingjest Nonez jakiegoś powodu lub nie istnieje, błędnie fałszywe lub „mniejsze” niż to, do czego naprawdę jest zdolny terminal lub strumień standardowego strumienia, spróbuj podać poprawny .encodingatrybut. W końcu zastępując sys.stdout & sys.stderrgo tłumaczącym obiektem podobnym do pliku.

  • Kiedy terminal / strumień nadal nie może zakodować wszystkich występujących znaków Unicode i nie chcesz z tego powodu łamać znaków, printmożesz wprowadzić zachowanie kodowania ze zamianą w tłumaczącym obiekcie podobnym do pliku.

Oto przykład:

#!/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()

Używanie zwykłych literałów łańcuchowych poza ascii w kodzie Python 2/2 + 3

Myślę, że jedynym dobrym powodem zmiany globalnego domyślnego kodowania (tylko na UTF-8) jest decyzja dotycząca kodu źródłowego aplikacji - a nie problemy z kodowaniem strumienia I / O: Do zapisywania literałów łańcuchowych poza ascii do kodu bez wymuszania aby zawsze używać u'string'ucieczki stylu Unicode. Można to zrobić dość konsekwentnie (pomimo tego, co mówi artykuł anonbadger ), dbając o podstawę kodu źródłowego Python 2 lub Python 2 + 3, który konsekwentnie używa zwykłych literałów ciągów ascii lub UTF-8 - o ile te ciągi potencjalnie podlegają wyciszeniu Konwersja Unicode i przechodzenie między modułami lub potencjalnie przejście do standardowego wyjścia. W tym celu preferuj „# encoding: utf-8"lub ascii (brak deklaracji). Zmień lub usuń biblioteki, które nadal polegają w bardzo głupi sposób na błędach domyślnego kodowania ascii poza chr # 127 (co jest dziś rzadkością).

I zrób to na starcie aplikacji (i / lub przez sitecustomize.py) jako dodatek do SmartStdoutpowyższego schematu - bez użycia 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

W ten sposób literały łańcuchowe i większość operacji (z wyjątkiem iteracji znaków) działają wygodnie, bez myślenia o konwersji Unicode, tak jakby istniał tylko Python3. Oczywiście we / wy pliku zawsze trzeba uważać na kodowanie - tak jak w Pythonie3.

Uwaga: łańcuchy SmartStdoutzwykłe są następnie niejawnie konwertowane z utf-8 do Unicode, zanim zostaną przekonwertowane na kodowanie strumienia wyjściowego.

kxr
źródło
4

Oto podejście, którego użyłem do stworzenia kodu, który był kompatybilny zarówno z python2, jak i python3 i zawsze dawał wyjście utf8 . Znalazłem tę odpowiedź gdzie indziej, ale nie pamiętam źródła.

To podejście działa poprzez zastąpienie sys.stdoutczymś, co nie jest całkiem podobne do pliku (ale nadal używa tylko rzeczy z biblioteki standardowej). Może to spowodować problemy dla twoich bazowych bibliotek, ale w prostym przypadku, gdy masz dobrą kontrolę nad tym, jak sys.stdout out jest używany w twoim frameworku, może to być rozsądne podejście.

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
Att Righ
źródło
3

To rozwiązało problem.

import os
os.environ["PYTHONIOENCODING"] = "utf-8"
twasbrillig
źródło
1

To jest szybki hack dla każdego, kto (1) na platformie Windows (2) używa Pythona 2.7 i (3) zirytowany, ponieważ ładne oprogramowanie (tj. Nie napisane przez ciebie, więc nie od razu kandydat do kodowania / dekodowania drukowania manewry) nie będą wyświetlać "ładnych znaków Unicode" w środowisku IDLE (Pythonwin drukuje dobrze unicode). Na przykład zgrabne symbole logiki pierwszego rzędu, których Stephan Boyer używa w wynikach swojego pedagogicznego dowodu w Prover Logic Pierwszego Porządku .

Nie podobał mi się pomysł wymuszenia ponownego załadowania sys i nie mogłem zmusić systemu do współpracy z ustawieniem zmiennych środowiskowych, takich jak PYTHONIOENCODING (wypróbowałem bezpośrednią zmienną środowiskową Windows, a także upuściłem ją w pliku sitecustomize.py w pakietach witryn jako jedną liner = 'utf-8').

Tak więc, jeśli chcesz zhakować swoją drogę do sukcesu, przejdź do katalogu IDLE, zazwyczaj: „C: \ Python27 \ Lib \ idlelib” Zlokalizuj plik IOBinding.py. Utwórz kopię tego pliku i zapisz go w innym miejscu, aby w razie potrzeby móc przywrócić oryginalne zachowanie. Otwórz plik w idlelib za pomocą edytora (np. IDLE). Przejdź do tego obszaru kodu:

# 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

Innymi słowy, zakomentuj oryginalną linię kodu następującą po `` try '', która sprawiła, że zmienna kodowania była równa locale.getdefaultlocale (ponieważ da ci to cp1252, którego nie chcesz) i zamiast tego brutalnie wymuś to na `` utf-8 '(dodając wiersz' encoding = 'utf-8 ', jak pokazano).

Uważam, że wpływa to tylko na wyświetlanie IDLE na standardowe wyjście, a nie na kodowanie używane dla nazw plików itp. (Które jest uzyskiwane we wcześniejszym kodowaniu systemu plików). Jeśli masz problem z jakimkolwiek innym kodem uruchomionym później w IDLE, po prostu zastąp plik IOBinding.py oryginalnym niezmodyfikowanym plikiem.

Dalton Bentley
źródło
1

Możesz zmienić kodowanie całego systemu operacyjnego. W Ubuntu możesz to zrobić za pomocą

sudo apt install locales 
sudo locale-gen en_US en_US.UTF-8    
sudo dpkg-reconfigure locales
Boris
źródło