Mengubah pengkodean default Python?

146

Saya memiliki banyak masalah "tidak dapat menyandikan" dan "tidak dapat memecahkan kode" dengan Python ketika saya menjalankan aplikasi saya dari konsol. Tapi di Eclipse PyDev IDE, pengkodean karakter default disetel ke UTF-8 , dan saya baik-baik saja.

Saya mencari-cari pengaturan pengkodean default, dan orang-orang mengatakan bahwa Python menghapus sys.setdefaultencodingfungsi saat startup, dan kami tidak dapat menggunakannya.

Jadi apa solusi terbaiknya?

Ali Nadalizadeh
sumber
1
Lihat entri blog The Illusive setdefaultencoding .
djc
3
The best solution is to learn to use encode and decode correctly instead of using hacks.Ini tentu saja dimungkinkan dengan python2 dengan biaya selalu mengingat untuk melakukannya / secara konsisten menggunakan antarmuka Anda sendiri. Pengalaman saya menunjukkan bahwa ini menjadi sangat bermasalah saat Anda menulis kode yang ingin Anda gunakan dengan python2 dan python3.
Att Righ

Jawaban:

165

Berikut adalah metode sederhana (peretasan) yang mengembalikan setdefaultencoding()fungsi yang telah dihapus dari sys:

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

(Catatan untuk Python 3.4+: reload()ada di importlibperpustakaan.)

Ini bukan hal yang aman untuk dilakukan : ini jelas merupakan peretasan, karena sys.setdefaultencoding()sengaja dihapus dari syssaat Python dimulai. Mengaktifkannya kembali dan mengubah pengkodean default dapat merusak kode yang bergantung pada ASCII sebagai default (kode ini dapat dari pihak ketiga, yang umumnya membuat perbaikan tidak mungkin atau berbahaya).

Eric O Lebigot
sumber
5
Saya downvoting, karena jawaban itu tidak membantu untuk menjalankan aplikasi yang sudah ada (yang merupakan salah satu cara untuk menafsirkan pertanyaan), salah saat Anda menulis / memelihara aplikasi dan berbahaya saat menulis perpustakaan. Cara yang benar adalah dengan mengatur LC_CTYPE(atau dalam sebuah aplikasi, periksa apakah sudah diatur dengan benar dan batalkan dengan pesan kesalahan yang berarti).
ibotty
@ibotty Saya setuju bahwa jawaban ini adalah retasan dan berbahaya untuk menggunakannya. Itu menjawab pertanyaan, meskipun ("Mengubah pengkodean default Python?"). Apakah Anda memiliki referensi tentang pengaruh variabel lingkungan LC_CTYPE pada interpreter Python?
Eric O Lebigot
yah, tidak disebutkan, ini peretasan pada awalnya. selain itu, jawaban berbahaya yang tidak disebutkan sama sekali, tidak membantu.
ibotty
1
@EOL kamu benar. Itu memang mempengaruhi LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
pengkodean
1
@ user2394901 Penggunaan sys.setdefaultencoding () selalu tidak disarankan !! Dan pengkodean py3k terprogram ke "utf-8" dan mengubahnya menimbulkan kesalahan.
Marlon Abeykoon
72

Jika Anda mendapatkan kesalahan ini saat mencoba menyalurkan / mengalihkan output skrip Anda

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

Cukup ekspor PYTHONIOENCODING di konsol dan kemudian jalankan kode Anda.

export PYTHONIOENCODING=utf8

Iman
sumber
3
Ini adalah satu-satunya solusi yang membuat perbedaan bagi saya. - Saya menggunakan Debian 7, dengan pengaturan lokal yang rusak. Terima kasih.
Pryo
4
Atur LC_CTYPEke sesuatu yang masuk akal sebagai gantinya. Itu membuat semua program lain senang juga.
ibotty
5
Bug yang lebih besar di Python3 adalah, itu PYTHONIOENCODING=utf8bukan defaultnya. Ini membuat skrip rusak hanya karenaLC_ALL=C
Tino
Set LC_CTYPE to something sensible insteadIni adalah saran yang masuk akal. Ini tidak berfungsi dengan baik ketika Anda mencoba mendistribusikan kode yang hanya berfungsi di sistem orang lain.
Att Righ
OS Debian dan Redhat menggunakan C.utf8lokal untuk menyediakan C. glibc upstream yang bekerja untuk menambahkannya, jadi mungkin kita tidak harus menyalahkan Python karena menghormati pengaturan lokal \…?
Mingye Wang
52

A) Untuk mengontrol sys.getdefaultencoding()keluaran:

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

ascii

Kemudian

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

dan

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

utf-16-be

Anda dapat menempatkan sitecustomize.py Anda lebih tinggi di PYTHONPATH.

Anda juga mungkin ingin mencoba reload(sys).setdefaultencoding@EOL

B) Untuk mengontrol stdin.encodingdan stdout.encodingAnda ingin mengatur PYTHONIOENCODING:

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

ascii ascii

Kemudian

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

utf-16-be utf-16-be

Terakhir: Anda dapat menggunakan A) atau B) atau keduanya!

lukmdo
sumber
(python2 saja) terpisah tapi menarik diperluas di atas dengan from __future__ import unicode_literalslihat diskusi
lukmdo
18

Dimulai dengan PyDev 3.4.1, pengkodean default tidak diubah lagi. Lihat tiket ini untuk detailnya.

Untuk versi sebelumnya, solusinya adalah memastikan PyDev tidak berjalan dengan UTF-8 sebagai pengkodean default. Di bawah Eclipse, jalankan pengaturan dialog ("jalankan konfigurasi", jika saya ingat dengan benar); Anda dapat memilih pengkodean default pada tab umum. Ubah ke US-ASCII jika Anda ingin mendapatkan kesalahan ini 'lebih awal' (dengan kata lain: di lingkungan PyDev Anda). Lihat juga entri blog asli untuk solusi ini .

ChristopheD
sumber
1
Terima kasih Chris. Apalagi mengingat komentar Mark T di atas, sepertinya jawaban Anda paling sesuai untuk saya. Dan untuk seseorang yang bukan pengguna utama Eclipse / PyDev, saya tidak akan pernah menyadarinya sendiri.
Sean
Saya ingin mengubahnya secara global (bukan sekali per konfigurasi yang dijalankan), tetapi belum mengetahui bagaimana - telah meminta q: stackoverflow.com/questions/9394277/…
Tim Diggins
13

Mengenai python2 (dan hanya python2), beberapa jawaban sebelumnya mengandalkan penggunaan peretasan berikut:

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

Tidak disarankan untuk menggunakannya (periksa ini atau ini )

Dalam kasus saya, ini datang dengan efek samping: Saya menggunakan notebook ipython, dan setelah saya menjalankan kode fungsi ´print´ tidak lagi berfungsi. Saya kira akan ada solusi untuk itu, tetapi tetap saya pikir menggunakan peretasan seharusnya bukan pilihan yang tepat.

Setelah mencoba banyak opsi, salah satu yang berhasil untuk saya adalah menggunakan kode yang sama di sitecustomize.py, di mana potongan kode itu seharusnya berada . Setelah mengevaluasi modul itu, fungsi setdefaultencoding dihapus dari sys.

Jadi solusinya adalah menambahkan ke file /usr/lib/python2.7/sitecustomize.pykode:

import sys
sys.setdefaultencoding('UTF8')

Ketika saya menggunakan virtualenvwrapper file yang saya edit adalah ~/.virtualenvs/venv-name/lib/python2.7/sitecustomize.py.

Dan ketika saya menggunakan dengan notebook python dan conda, itu benar ~/anaconda2/lib/python2.7/sitecustomize.py

Kiril
sumber
8

Ada posting blog yang berwawasan tentang itu.

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

Saya memparafrasekan isinya di bawah ini.

Dalam python 2 yang tidak diketik dengan kuat mengenai pengkodean string, Anda dapat melakukan operasi pada string yang dikodekan berbeda, dan berhasil. Misalnya, berikut ini akan kembali True.

u'Toshio' == 'Toshio'

Itu akan berlaku untuk setiap string (normal, tidak difiksasi) yang dikodekan sys.getdefaultencoding(), yang menjadi default ascii, tetapi tidak yang lain.

Pengkodean default dimaksudkan untuk diubah di seluruh sistem site.py, tetapi tidak di tempat lain. Peretasan (juga disajikan di sini) untuk mengaturnya dalam modul pengguna hanyalah: peretasan, bukan solusinya.

Python 3 memang mengubah pengkodean sistem ke default ke utf-8 (ketika LC_CTYPE sadar-unicode), tetapi masalah mendasar diselesaikan dengan persyaratan untuk secara eksplisit menyandikan string "byte" setiap kali mereka digunakan dengan string unicode.

ibotty
sumber
4

Pertama: reload(sys)dan menyetel beberapa pengkodean default acak hanya terkait kebutuhan aliran terminal keluaran adalah praktik yang buruk. reloadsering mengubah hal-hal dalam sys yang telah diterapkan tergantung pada lingkungan - misalnya aliran sys.stdin / stdout, sys.excepthook, dll.

Memecahkan masalah encode di stdout

Solusi terbaik yang saya tahu untuk memecahkan masalah encode dari printstring unicode dan di luar-ascii str(misalnya dari literals) di sys.stdout adalah: menjaga sys.stdout (objek seperti file) yang mampu dan secara opsional toleran terhadap kebutuhan:

  • Ketika sys.stdout.encodingini Noneuntuk beberapa alasan, atau non-ada, atau keliru palsu atau "kurang" dari apa yang stdout terminal atau streaming benar-benar mampu, kemudian mencoba untuk memberikan yang benar .encodingatribut. Akhirnya dengan mengganti sys.stdout & sys.stderrdengan objek seperti file terjemahan.

  • Ketika terminal / stream masih tidak dapat menyandikan semua karakter unicode yang terjadi, dan ketika Anda tidak ingin merusaknya printhanya karena itu, Anda dapat memperkenalkan perilaku encode-with-replace dalam menerjemahkan objek seperti file.

Berikut contohnya:

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

Menggunakan literal string biasa di luar ascii dengan kode Python 2/2 + 3

Satu-satunya alasan yang baik untuk mengubah pengkodean default global (ke UTF-8 saja) menurut saya adalah mengenai keputusan kode sumber aplikasi - dan bukan karena masalah pengkodean aliran I / O: Untuk menulis literal string ascii ke dalam kode tanpa dipaksa untuk selalu menggunakan u'string'pelolosan unicode gaya. Hal ini dapat dilakukan secara agak konsisten (terlepas dari apa yang anonbadger artikel ) dengan menjaga basis kode sumber Python 2 atau Python 2 + 3 yang menggunakan literal string biasa ascii atau UTF-8 secara konsisten - sejauh string tersebut berpotensi menjalani silent unicode dan berpindah antar modul atau berpotensi pergi ke stdout. Untuk itu, pilih "# encoding: utf-8 "atau ascii (tanpa deklarasi). Ubah atau jatuhkan pustaka yang masih mengandalkan dengan cara yang sangat bodoh secara fatal pada kesalahan pengkodean default ascii di luar chr # 127 (yang jarang terjadi saat ini).

Dan lakukan seperti ini pada permulaan aplikasi (dan / atau melalui sitecustomize.py) selain SmartStdoutskema di atas - tanpa menggunakan 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

Dengan cara ini string literal dan sebagian besar operasi (kecuali iterasi karakter) bekerja dengan nyaman tanpa memikirkan konversi unicode seolah-olah hanya akan ada Python3. File I / O tentu saja selalu membutuhkan perhatian khusus terkait pengkodean - seperti pada Python3.

Catatan: string dataran kemudian secara implisit diubah dari utf-8 menjadi unicode SmartStdoutsebelum diubah menjadi enconding aliran keluaran.

kxr
sumber
4

Berikut adalah pendekatan yang saya gunakan untuk menghasilkan kode yang kompatibel dengan python2 dan python3 dan selalu menghasilkan keluaran utf8 . Saya menemukan jawaban ini di tempat lain, tetapi saya tidak dapat mengingat sumbernya.

Pendekatan ini bekerja dengan mengganti sys.stdoutdengan sesuatu yang tidak seperti file (tetapi masih hanya menggunakan hal-hal di pustaka standar). Ini mungkin menyebabkan masalah untuk pustaka yang mendasari Anda, tetapi dalam kasus sederhana di mana Anda memiliki kontrol yang baik atas bagaimana sys.stdout out digunakan melalui kerangka kerja Anda, ini bisa menjadi pendekatan yang masuk akal.

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
Att Righ
sumber
1

Ini adalah peretasan cepat untuk siapa saja yang (1) Pada platform Windows (2) menjalankan Python 2.7 dan (3) kesal karena perangkat lunak yang bagus (yaitu, tidak ditulis oleh Anda sehingga tidak segera menjadi kandidat untuk pencetakan encode / decode manuver) tidak akan menampilkan "karakter unicode cantik" di lingkungan IDLE (Pythonwin mencetak unicode fine), Misalnya, simbol Logika Urutan Pertama rapi yang digunakan Stephan Boyer dalam keluaran dari penguji pedagogiknya di Penguji Logika Urutan Pertama .

Saya tidak suka gagasan memaksa sys reload dan saya tidak bisa mendapatkan sistem untuk bekerja sama dengan pengaturan variabel lingkungan seperti PYTHONIOENCODING (mencoba variabel lingkungan Windows langsung dan juga menjatuhkannya di sitecustomize.py di paket situs sebagai satu liner = 'utf-8').

Jadi, jika Anda ingin meretas jalan Anda menuju sukses, buka direktori IDLE Anda, biasanya: "C: \ Python27 \ Lib \ idlelib" Temukan file IOBinding.py. Buat salinan file itu dan simpan di tempat lain sehingga Anda dapat kembali ke perilaku asli saat Anda memilih. Buka file di idlelib dengan editor (misalnya, IDLE). Pergi ke area kode ini:

# 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

Dengan kata lain, komentari baris kode asli setelah ' coba ' yang membuat variabel pengkodean sama dengan locale.getdefaultlocale (karena itu akan memberi Anda cp1252 yang tidak Anda inginkan) dan sebagai gantinya paksa paksa ke 'utf-8 '(dengan menambahkan baris' encoding = 'utf-8 ' seperti yang ditunjukkan).

Saya percaya ini hanya mempengaruhi tampilan IDLE ke stdout dan bukan pengkodean yang digunakan untuk nama file, dll. (Yang diperoleh di filesystemencoding sebelumnya). Jika Anda memiliki masalah dengan kode lain yang Anda jalankan di IDLE nanti, cukup ganti file IOBinding.py dengan file asli yang tidak dimodifikasi.

Dalton Bentley
sumber
1

Ini memperbaiki masalah saya.

import os
os.environ["PYTHONIOENCODING"] = "utf-8"
twasbrillig.dll
sumber
Tidak untuk saya. Tetapi bekerja saat mengekspor variabel di shell sebelum memasukkan python, atau menggunakan reload (sys); sys.defaultencoding ("utf-8").
Eric H.
Juga tidak berhasil untukku.
MikeB
1

Anda dapat mengubah pengkodean seluruh sistem operasi Anda. Di Ubuntu Anda dapat melakukan ini dengan

sudo apt install locales 
sudo locale-gen en_US en_US.UTF-8    
sudo dpkg-reconfigure locales
Boris
sumber