Çeşitli cevaplardan parçalar ve parçalar sayesinde, bir açıklama yapabiliriz.
Bir unicode dize olan u '\ xe9' yazdırmaya çalışarak, Python sys.stdout.encoding içinde saklanan kodlama şemasını kullanarak bu dizeyi örtük olarak kodlamaya çalışır. Python aslında bu ayarı başlatıldığı ortamdan alır. Ortamdan uygun bir kodlama bulamazsa, ancak o zaman varsayılan ASCII'ye geri döner.
Örneğin, varsayılan olarak UTF-8 kodlayan bir bash kabuğu kullanıyorum. Python'u ondan başlatırsam, alır ve bu ayarı kullanır:
$ python
>>> import sys
>>> print sys.stdout.encoding
UTF-8
Bir an için Python kabuğundan çıkalım ve bash ortamını bazı sahte kodlamalarla ayarlayalım:
$ export LC_CTYPE=klingon
# we should get some error message here, just ignore it.
Sonra python kabuğunu tekrar başlatın ve gerçekten varsayılan ascii kodlamasına geri döndüğünü doğrulayın.
$ python
>>> import sys
>>> print sys.stdout.encoding
ANSI_X3.4-1968
Bingo!
Şimdi ascii dışında bazı unicode karakterler çıkarmaya çalışırsanız, güzel bir hata mesajı almalısınız
>>> print u'\xe9'
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9'
in position 0: ordinal not in range(128)
Python'dan çıkıp bash kabuğunu atalım.
Şimdi Python dizeleri çıkardıktan sonra ne olacağını gözlemleyeceğiz. Bunun için önce bir grafik terminali içinde bir bash kabuğu başlatacağız (Gnome Terminalini kullanıyorum) ve terminali çıktıyı ISO-8859-1 aka latin-1 ile deşifre edecek şekilde ayarlayacağız (grafik terminallerinin genellikle Karakter Ayarlama seçeneği vardır Açılır menülerinden birinde kodlama ). Bunun gerçek kabuk ortamının kodlamasını değiştirmediğini, yalnızca terminalin kendisinin verdiği çıkışı çözme şeklini değiştirdiğini, bir web tarayıcısının yaptığı gibi değiştirdiğini unutmayın. Bu nedenle terminalin kodlamasını kabuk ortamından bağımsız olarak değiştirebilirsiniz. Daha sonra Python'u kabuktan başlatalım ve sys.stdout.encoding öğesinin kabuk ortamının kodlamasına (benim için UTF-8) ayarlandığını doğrulayalım:
$ 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 ikili dizeyi olduğu gibi çıkarır, terminal onu alır ve değerini latin-1 karakter eşlemesiyle eşleştirmeye çalışır. Latince-1'de 0xe9 veya 233, "é" karakterini verir ve bu yüzden terminalin gösterdiği şey budur.
(2) python , Unicode dizesini sys.stdout.encoding öğesinde ayarlı olan şema ile örtük olarak kodlamaya çalışır , bu örnekte "UTF-8" olur. UTF-8 kodlamasından sonra, elde edilen ikili dize '\ xc3 \ xa9' dur (daha sonra açıklamaya bakınız). Terminal akışı bu şekilde alır ve latin-1 kullanarak 0xc3a9 kodunu çözmeye çalışır, ancak latin-1 0'dan 255'e gider ve böylece bir seferde yalnızca 1 baytlık kodları çözer. 0xc3a9 2 bayt uzunluğundadır, latin-1 kod çözücü 0xc3 (195) ve 0xa9 (169) olarak yorumlar ve bu 2 karakter verir: Ã ve ©.
(3) python unicode kod noktasını u '\ xe9' (233) latin-1 şemasıyla kodlar. Latin-1 kod noktaları aralığının 0-255 olduğu ve bu aralıktaki Unicode ile aynı karakteri işaret ettiği ortaya çıktı. Bu nedenle, bu aralıktaki Unicode kod noktaları latin-1'de kodlandığında aynı değeri verir. Yani latin-1 ile kodlanmış u '\ xe9' (233) ikili '\ xe9' dizesini de verecektir. Terminal bu değeri alır ve latin-1 karakter haritasında eşleştirmeye çalışır. Tıpkı durum (1) gibi "é" verir ve görüntülenen de budur.
Şimdi, açılır menüden terminalin kodlama ayarlarını UTF-8 olarak değiştirelim (web tarayıcınızın kodlama ayarlarını değiştirir gibi). Python'u durdurmaya veya kabuğu yeniden başlatmaya gerek yok. Terminalin kodlaması artık Python'larla eşleşiyor. Tekrar yazdırmayı deneyelim:
>>> print '\xe9' # (4)
>>> print u'\xe9' # (5)
é
>>> print u'\xe9'.encode('latin-1') # (6)
>>>
(4) python olduğu gibi bir ikili dize çıkarır . Terminal UTF-8 ile bu akışı çözmeyi dener. Ancak UTF-8, 0xe9 değerini anlamıyor (daha sonra açıklamaya bakınız) ve bu nedenle onu bir unicode kod noktasına dönüştüremiyor. Kod noktası bulunamadı, hiçbir karakter yazdırılmadı.
(5) python , Unicode dizesini sys.stdout.encoding içindeki ile örtük olarak kodlamaya çalışır . Hala "UTF-8". Sonuçta elde edilen ikili dize '\ xc3 \ xa9'dur. Terminal akışı alır ve UTF-8 kullanarak 0xc3a9 kodunu çözmeye çalışır. Unicode karakter eşlemesinde "é" sembolünü gösteren 0xe9 (233) kod değerini verir. Terminal "é" görüntüler.
(6) python unicode dizgiyi latin-1 ile kodlar, aynı değer '\ xe9' ile bir ikili dizge verir. Yine, terminal için bu durum (4) ile hemen hemen aynıdır.
Sonuçlar: - Python, varsayılan kodlamasını dikkate almadan unicode olmayan dizeleri ham veri olarak çıkarır. Terminal, geçerli kodlaması verilerle eşleşirse bunları görüntüler. - Python, Unicode dizelerini sys.stdout.encoding öğesinde belirtilen şemayı kullanarak kodladıktan sonra çıkarır. - Python bu ayarı kabuğun ortamından alır. - terminal çıkışı kendi kodlama ayarlarına göre görüntüler. - terminalin kodlaması kabuktan bağımsızdır.
Unicode, UTF-8 ve latin-1 hakkında daha fazla bilgi:
Unicode temel olarak bazı simgelere işaret etmek için bazı tuşların (kod noktaları) geleneksel olarak atandığı bir karakter tablosudur. örneğin, kural olarak 0xe9 (233) anahtarının 'é' sembolüne işaret eden değer olduğuna karar verilmiştir. ASCII ve Unicode, 0 ile 127 arasında aynı kod noktalarını, latin-1 ve Unicode ile 0 ile 255 arasında aynı kodu kullanır. Yani, ASCII, latin-1 ve Unicode'da 0x41, 'C', 0xc8'de 'Ü' latin-1 ve Unicode, 0xe9, latin-1 ve Unicode'da 'é'yi gösterir.
Elektronik cihazlarla çalışırken, Unicode kod noktalarının elektronik olarak gösterilmesi için etkili bir yol gerekir. Kodlama şemaları budur. Çeşitli Unicode kodlama şemaları vardır (utf7, UTF-8, UTF-16, UTF-32). En sezgisel ve doğrudan kodlama yaklaşımı, Unicode haritasında bir kod noktasının değerini, elektronik formu için değeri olarak kullanmak olacaktır, ancak Unicode şu anda bir milyondan fazla kod noktasına sahiptir, bu da bazılarının 3 bayt olmasını gerektirir. olarak ifade edilmiştir. Metinle verimli bir şekilde çalışmak için 1'e 1 eşleme oldukça pratik olmayacaktır, çünkü tüm kod noktalarının gerçek gereksinimlerine bakılmaksızın, karakter başına minimum 3 bayt ile tam olarak aynı miktarda alanda depolanmasını gerektirecektir.
Çoğu kodlama şemasının alan gereksinimi ile ilgili eksiklikleri vardır, en ekonomik olanları tüm unicode kod noktalarını kapsamaz, örneğin ascii sadece ilk 128'i kaplarken, latin-1 ilk 256'yı kapsar. Daha kapsamlı olmaya çalışan diğerleri de sona erer israf etmek, ortak "ucuz" karakterler için bile gerekenden daha fazla bayt gerektirdiğinden. Örneğin UTF-16, ascii aralığındaki karakterler de dahil olmak üzere karakter başına minimum 2 bayt kullanır (65 olan 'B', UTF-16'da hala 2 bayt depolama gerektirir). UTF-32, tüm karakterleri 4 baytta sakladığından daha da israflıdır.
UTF-8, ikilemi akıllıca çözdü ve şema, kod noktalarını değişken miktarda bayt boşluğuyla saklayabildi. Kodlama stratejisinin bir parçası olarak UTF-8, kod noktalarını, alan gereksinimlerini ve sınırlarını (muhtemelen kod çözücülere) gösteren bayrak bitleriyle bağlar.
ASCII aralığında (0-127) unicode kod noktalarının UTF-8 kodlaması:
0xxx xxxx (in binary)
- x'ler kodlama sırasında kod noktasını "saklamak" için ayrılan gerçek alanı gösterir
- Baştaki 0, UTF-8 kod çözücüsüne bu kod noktasının yalnızca 1 bayt gerektireceğini gösteren bir işarettir.
- kodlama üzerine UTF-8, belirli bir aralıktaki kod noktalarının değerini değiştirmez (yani UTF-8'de kodlanan 65 de 65'tir). Unicode ve ASCII'nin aynı aralıkta uyumlu olduğu düşünüldüğünde, tesadüfen UTF-8 ve ASCII'yi de bu aralıkta uyumlu hale getirir.
örneğin 'B' için Unicode kod noktası ikili dosyada '0x42' veya 0100 0010'dur (dediğimiz gibi ASCII'de aynıdır). UTF-8 kodlamasından sonra:
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)
127'nin üzerindeki Unicode kod noktalarının UTF-8 kodlaması (ascii olmayan):
110x xxxx 10xx xxxx <-- (from 128 to 2047)
1110 xxxx 10xx xxxx 10xx xxxx <-- (from 2048 to 65535)
- baştaki '110' bitleri UTF-8 kod çözücüsüne 2 baytta kodlanmış bir kod noktasının başlangıcını belirtirken, '1110' 3 bayt belirtir, 11110 4 bayt ve benzerlerini gösterir.
- iç '10' bayrak bitleri bir iç baytın başlangıcını belirtmek için kullanılır.
- yine x'ler, kodlamadan sonra Unicode kod noktası değerinin depolandığı alanı işaretler.
örneğin 'é' Unicode kod noktası 0xe9'dur (233).
1110 1001 <-- 0xe9
UTF-8 bu değeri kodladığında, değerin 127'den büyük ve 2048'den küçük olduğunu belirler, bu nedenle 2 baytta kodlanmalıdır:
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
UTF-8 kodlaması 0xc3a9 olduktan sonra 0xe9 Unicode kodu işaret eder. Terminal tam olarak böyle alır. Terminaliniz latin-1 (unicode olmayan eski kodlamalardan biri) kullanarak dizeleri deşifre edecek şekilde ayarlanmışsa, Ã © görürsünüz, çünkü latin-1'deki 0xc3, Ã ve 0xa9 ila © işaret eder.