Berkat potongan-potongan dari berbagai balasan, saya pikir kita bisa menjahit penjelasan.
Dengan mencoba mencetak string unicode, u '\ xe9', Python secara implisit mencoba untuk menyandikan string itu menggunakan skema penyandian yang saat ini disimpan di sys.stdout.encoding. Python sebenarnya mengambil pengaturan ini dari lingkungannya. Jika itu tidak dapat menemukan pengkodean yang tepat dari lingkungan, hanya kemudian kembali ke standarnya , ASCII.
Sebagai contoh, saya menggunakan bash shell yang secara default encoding ke UTF-8. Jika saya memulai Python dari itu, ia mengambil dan menggunakan pengaturan itu:
$ python
>>> import sys
>>> print sys.stdout.encoding
UTF-8
Mari sesaat keluar dari shell Python dan atur lingkungan bash dengan beberapa penyandian palsu:
$ export LC_CTYPE=klingon
# we should get some error message here, just ignore it.
Kemudian mulai shell python lagi dan verifikasi bahwa memang mengembalikan ke ascii default.
$ python
>>> import sys
>>> print sys.stdout.encoding
ANSI_X3.4-1968
Bingo!
Jika sekarang Anda mencoba untuk mengeluarkan beberapa karakter unicode di luar ascii Anda akan mendapatkan pesan kesalahan yang bagus
>>> print u'\xe9'
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9'
in position 0: ordinal not in range(128)
Ayo keluar dari Python dan buang bash shell.
Kami sekarang akan mengamati apa yang terjadi setelah Python mengeluarkan string. Untuk ini pertama-tama kita akan memulai bash shell dalam terminal grafik (saya menggunakan Terminal Gnome) dan kami akan mengatur terminal untuk mendekode output dengan ISO-8859-1 alias latin-1 (terminal grafik biasanya memiliki opsi untuk Mengatur Karakter Pengkodean di salah satu menu dropdown mereka). Perhatikan bahwa ini tidak mengubah pengkodean lingkungan shell yang sebenarnya , itu hanya mengubah cara terminal itu sendiri akan mendekode output yang diberikan, sedikit seperti browser web. Karena itu Anda dapat mengubah pengkodean terminal, secara independen dari lingkungan shell. Mari kita mulai Python dari shell dan verifikasi bahwa sys.stdout.encoding diatur ke pengkodean lingkungan shell (UTF-8 untuk saya):
$ python
>>> import sys
>>> print sys.stdout.encoding
UTF-8
>>> print '\xe9' # (1)
é
>>> print u'\xe9' # (2)
é
>>> print u'\xe9'.encode('latin-1') # (3)
é
>>>
(1) python mengeluarkan string biner, terminal menerimanya dan mencoba mencocokkan nilainya dengan peta karakter latin-1. Dalam bahasa latin-1, 0xe9 atau 233 menghasilkan karakter "é" dan itulah yang ditampilkan terminal.
(2) python mencoba menyandikan string Unicode secara implisit dengan skema apa pun yang saat ini ditetapkan dalam sys.stdout.encoding, dalam hal ini "UTF-8". Setelah pengkodean UTF-8, string biner yang dihasilkan adalah '\ xc3 \ xa9' (lihat penjelasan selanjutnya). Terminal menerima aliran seperti itu dan mencoba untuk men-decode 0xc3a9 menggunakan latin-1, tetapi latin-1 beralih dari 0 ke 255 dan karenanya, hanya mendekode stream 1 byte pada satu waktu. 0xc3a9 panjangnya 2 byte, oleh karena itu decoder latin-1 menerjemahkannya sebagai 0xc3 (195) dan 0xa9 (169) dan menghasilkan 2 karakter: Ã dan ©.
(3) python mengkodekan titik kode unicode u '\ xe9' (233) dengan skema latin-1. Ternyata rentang kode titik latin-1 adalah 0-255 dan menunjuk ke karakter yang sama persis dengan Unicode dalam rentang itu. Oleh karena itu, titik kode Unicode dalam rentang itu akan menghasilkan nilai yang sama ketika dikodekan dalam latin-1. Jadi u '\ xe9' (233) yang dikodekan dalam latin-1 juga akan menghasilkan string biner '\ xe9'. Terminal menerima nilai itu dan mencoba mencocokkannya pada peta karakter latin-1. Sama seperti kasus (1), ia menghasilkan "é" dan itulah yang ditampilkan.
Sekarang mari kita ubah pengaturan penyandian terminal ke UTF-8 dari menu dropdown (seperti Anda akan mengubah pengaturan penyandian browser web Anda). Tidak perlu menghentikan Python atau memulai ulang shell. Pengkodean terminal sekarang cocok dengan Python. Mari kita coba mencetak lagi:
>>> print '\xe9' # (4)
>>> print u'\xe9' # (5)
é
>>> print u'\xe9'.encode('latin-1') # (6)
>>>
(4) python menampilkan string biner apa adanya. Terminal mencoba untuk men-decode aliran itu dengan UTF-8. Tetapi UTF-8 tidak memahami nilai 0xe9 (lihat penjelasan selanjutnya) dan karena itu tidak dapat mengubahnya menjadi titik kode unicode. Tidak ditemukan titik kode, tidak ada karakter yang dicetak.
(5) python mencoba menyandikan string Unicode secara implisit dengan apa pun yang ada di sys.stdout.encoding. Masih "UTF-8". String biner yang dihasilkan adalah '\ xc3 \ xa9'. Terminal menerima aliran dan mencoba untuk memecahkan kode 0xc3a9 juga menggunakan UTF-8. Ini menghasilkan kembali nilai kode 0xe9 (233), yang pada peta karakter Unicode menunjuk ke simbol "é". Terminal menampilkan "é".
(6) python mengkodekan string unicode dengan latin-1, menghasilkan string biner dengan nilai yang sama '\ xe9'. Sekali lagi, untuk terminal ini hampir sama dengan case (4).
Kesimpulan: - Python menampilkan string non-unicode sebagai data mentah, tanpa mempertimbangkan penyandian defaultnya. Terminal kebetulan menampilkannya jika pengkodeannya saat ini cocok dengan data. - Python mengeluarkan string Unicode setelah menyandikannya menggunakan skema yang ditentukan dalam sys.stdout.encoding. - Python mendapatkan pengaturan itu dari lingkungan shell. - terminal menampilkan output sesuai dengan pengaturan pengkodeannya sendiri. - Pengkodean terminal independen dari shell.
Lebih detail tentang unicode, UTF-8 dan latin-1:
Unicode pada dasarnya adalah tabel karakter di mana beberapa tombol (titik kode) telah secara konvensional ditugaskan untuk menunjuk ke beberapa simbol. misalnya dengan konvensi, telah diputuskan bahwa kunci 0xe9 (233) adalah nilai yang menunjuk ke simbol 'é'. ASCII dan Unicode menggunakan titik kode yang sama dari 0 hingga 127, seperti halnya latin-1 dan Unicode dari 0 hingga 255. Artinya, 0x41 menunjuk ke 'A' di ASCII, latin-1 dan Unicode, 0xc8 menunjuk ke 'Ü' di latin-1 dan Unicode, 0xe9 menunjuk ke 'é' dalam latin-1 dan Unicode.
Saat bekerja dengan perangkat elektronik, titik kode Unicode memerlukan cara yang efisien untuk direpresentasikan secara elektronik. Tentang itulah skema penyandian. Berbagai skema pengkodean Unicode ada (utf7, UTF-8, UTF-16, UTF-32). Pendekatan pengkodean yang paling intuitif dan lurus ke depan adalah dengan hanya menggunakan nilai titik kode dalam peta Unicode sebagai nilai untuk bentuk elektroniknya, tetapi Unicode saat ini memiliki lebih dari satu juta titik kode, yang berarti bahwa beberapa di antaranya memerlukan 3 byte untuk menjadi menyatakan. Untuk bekerja secara efisien dengan teks, pemetaan 1 ke 1 akan agak tidak praktis, karena akan mengharuskan semua titik kode disimpan dalam jumlah ruang yang persis sama, dengan minimal 3 byte per karakter, terlepas dari kebutuhan mereka yang sebenarnya.
Sebagian besar skema pengkodean memiliki kekurangan mengenai persyaratan ruang, yang paling ekonomis tidak mencakup semua poin kode unicode, misalnya ascii hanya mencakup 128 pertama, sedangkan latin-1 mencakup 256 pertama. Lainnya yang mencoba menjadi lebih komprehensif akhirnya juga menjadi boros, karena mereka membutuhkan lebih banyak byte daripada yang diperlukan, bahkan untuk karakter "murah" yang umum. UTF-16 misalnya, menggunakan minimal 2 byte per karakter, termasuk yang berada dalam rentang ascii ('B' yang 65, masih membutuhkan 2 byte penyimpanan di UTF-16). UTF-32 bahkan lebih boros karena menyimpan semua karakter dalam 4 byte.
UTF-8 secara cerdik menyelesaikan dilema, dengan skema yang dapat menyimpan titik kode dengan jumlah variabel ruang byte. Sebagai bagian dari strategi pengkodeannya, UTF-8 bertali poin kode dengan bit bendera yang menunjukkan (mungkin untuk decoder) persyaratan ruang mereka dan batas-batasnya.
Pengkodean poin kode unicode UTF-8 dalam rentang ascii (0-127):
0xxx xxxx (in binary)
- tanda x menunjukkan ruang aktual yang disediakan untuk "menyimpan" titik kode selama penyandian
- 0 yang terdepan adalah tanda yang menunjukkan ke decoder UTF-8 bahwa titik kode ini hanya akan membutuhkan 1 byte.
- pada saat encoding, UTF-8 tidak mengubah nilai poin kode dalam rentang spesifik tersebut (yaitu 65 yang dikodekan dalam UTF-8 juga 65). Mempertimbangkan bahwa Unicode dan ASCII juga kompatibel dalam rentang yang sama, itu secara kebetulan membuat UTF-8 dan ASCII juga kompatibel dalam kisaran itu.
mis. Titik kode Unicode untuk 'B' adalah '0x42' atau 0100 0010 dalam biner (seperti yang kami katakan, itu sama di ASCII). Setelah encoding di UTF-8 menjadi:
0xxx xxxx <-- UTF-8 encoding for Unicode code points 0 to 127
*100 0010 <-- Unicode code point 0x42
0100 0010 <-- UTF-8 encoded (exactly the same)
Pengkodean poin kode Unicode UTF-8 di atas 127 (non-ascii):
110x xxxx 10xx xxxx <-- (from 128 to 2047)
1110 xxxx 10xx xxxx 10xx xxxx <-- (from 2048 to 65535)
- bit terkemuka '110' menunjukkan ke decoder UTF-8 awal dari titik kode yang dikodekan dalam 2 byte, sedangkan '1110' menunjukkan 3 byte, 11110 akan menunjukkan 4 byte dan sebagainya.
- bit bendera '10' bagian dalam digunakan untuk memberi sinyal awal byte batin.
- lagi, tanda x adalah spasi tempat nilai titik kode Unicode disimpan setelah penyandian.
misalnya 'é' Titik kode Unicode adalah 0xe9 (233).
1110 1001 <-- 0xe9
Ketika UTF-8 mengkodekan nilai ini, ini menentukan bahwa nilainya lebih besar dari 127 dan kurang dari 2048, oleh karena itu harus dikodekan dalam 2 byte:
110x xxxx 10xx xxxx <-- UTF-8 encoding for Unicode 128-2047
***0 0011 **10 1001 <-- 0xe9
1100 0011 1010 1001 <-- 'é' after UTF-8 encoding
C 3 A 9
Kode Unicode 0xe9 menunjuk setelah pengkodean UTF-8 menjadi 0xc3a9. Persis bagaimana terminal menerimanya. Jika terminal Anda diatur untuk mendekodekan string menggunakan latin-1 (salah satu pengkodean warisan non-unicode), Anda akan melihat à ©, karena kebetulan 0xc3 dalam bahasa latin-1 menunjuk ke à dan 0xa9 ke ©.