Zanurkuj w Pythonie/Kodowanie znaków
Z Wikibooks, biblioteki wolnych podręczników.
Spis treści |
[edytuj] Kodowanie znaków
W komputerze pewnym znakom odpowiadają pewne liczby, a kodowanie znaków określa, która liczba odpowiada jakiej literze. W łańcuchu znaków każdy symbol zajmuje 8 bitów, co daje nam do dyspozycji tylko 256 różnych symboli. Podstawowym systemem kodowania jest ASCII. Przyporządkowuje on liczbom z zakresu od 0 do 127 znaki alfabetu angielskiego, cyfry i niektóre inne symbole. Pozostałe standardowe systemy kodowania rozszerzają standard ASCII, dlatego znaki z przedziału od 0 do 127 w każdym systemie kodowania są takie same.
>>> ord('a') #(1)
97
>>> chr(99) #(2)
'c'
>>> ord('%')
37 #(3)
>>> chr(115)
's'
>>> chr(261)
Traceback (most recent call last): #(4)
File "<stdin>", line 1, in ?
ValueError: chr() arg not in range(261)
>>> chr(188)
'\xbc' #(5)
- Funkcja
ordzwraca liczbę, która odpowiada danemu symbolowi. W tym przypadku literze "a" odpowiada liczba 97. - Za pomocą funkcji
chrdowiadujemy się, jaki znak odpowiada danej liczbie. Liczbie 99 odpowiada znak "c". - Procent ("%") odpowiada liczbie 37.
- Każdy symbol odpowiada liczbie z zakresu od 0 do 255. Liczba 261 nie mieści się w jednym bajcie, dlatego wyskoczył nam wyjątek.
- Co prawda liczba 188 mieści się w 8-bitach, ale nie mieści się w standardzie ASCII i dlatego tego symbolu Python nie może jednoznacznie zinterpretować. W systemie kodowania ISO 8859-2 liczba ta odpowiada znakowi "ź", ale w systemie kodowania Windows-1250 (znany też jako CP-1250) znakowi "Ľ".
Każdy edytor tekstu zapisuje tworzone przez nas programy korzystając z jakiegoś kodowania, choćby z samego ASCII. Dobrze jest korzystać z edytora, który daje nam możliwość ustawienia kodowania znaków. Kiedy wiemy, w jakim systemie kodowania został zapisany nasz skrypt, powinniśmy jeszcze o tym poinformować Pythona.
[edytuj] Informowanie Pythona o kodowaniu znaków
Wróćmy do odbchelper.py. Na samym początku dodaliśmy linię [1]:
#-*- coding: utf-8 -*-
W ten sposób ustawiamy kodowanie znaków danego pliku, a nie całego programu (program może się składać z wielu plików). Zresztą, jeśli nie zdefiniujemy kodowania znaków, Python nas o tym uprzedzi:
sys:1: DeprecationWarning: Non-ASCII character '\xc5' in file test.py on line 5
but no encoding declared; see http://www.python.org/peps/pep-0263.html for detils
Jeśli skorzystaliśmy z innego kodowania znaków, zamiast utf-8 oczywiście napiszemy coś innego. Dodając polskie znaki z reguły korzysta się z kodowania UTF-8 lub ISO-8859-2, a czasami w przypadku Windowsa z Windows-1250.
Ale co wtedy, gdy nie mamy możliwości ustawić kodowania znaków i nie wiemy z jakiego korzysta nasz edytor? Można to sprawdzić metodą prób i błędów:
#-*- coding: {tu wstawiamy utf-8, iso-8859-2 lub windows-1250} -*-
print "zażółć gęślą jaźń"
A może pora zmienić edytor?
[edytuj] Unikod jeszcze raz
Jak wiemy, unikod jest systemem reprezentowania znaków ze wszystkich różnych języków świata.
Zaraz powrócimy do Pythona.
Notatka historyczna. Przed powstaniem unikodu istniały oddzielne systemy kodowania znaków dla każdego języka, a co przed chwilą trochę omówiliśmy. Każdy z nich korzystał z tych samych liczb (0-255) do reprezentowania znaków danego języka. Niektóre języki (jak rosyjski) miały wiele sprzecznych standardów reprezentowania tych samych znaków. Inne języki (np. japoński) posiadają tak wiele znaków, że wymagają wielu bajtów, aby zapisać cały zbiór jego znaków. Wymiana dokumentów pomiędzy tymi systemami była trudna, ponieważ komputer nie mógł stwierdzić, z którego systemu kodowania skorzystał autor. Komputer widział tylko liczby, a liczby mogą oznaczać różne rzeczy. Zaczęto się zastanawiać nad przechowywaniem tych dokumentów w tym samym miejscu (np. w tej samej tabeli bazy danych); trzeba było przechowywać informacje o kodowaniu każdego kawałku tekstu, a także trzeba było za każdym razem informować o kodowaniu przekazywanego tekstu. Wtedy też zaczęto myśleć o wielojęzycznych dokumentach, w których znaki z wielu języków znajdują się w tym samym dokumencie. (Wykorzystywały one zazwyczaj kod ucieczki, aby przełączyć tryb kodowania; ciach, jesteś w rosyjskim trybie, więc 241 znaczy to; ciach, jesteś w greckim trybie, więc 241 znaczy coś innego itd.) Unikod został zaprojektowany po to, aby rozwiązywać tego typu problemy.
Aby rozwiązać te problemy, unikod reprezentuje wszystkie znaki jako 2-bajtowe liczby, od 0 do 65535 [2] Każda 2-bajtowa liczba reprezentuje unikalny znak, który jest wykorzystywany w co najmniej jednym języku świata. (Znaki które są wykorzystywane w wielu językach świata, mają ten sam kod numeryczny.) Mamy dokładnie jedną liczbę na znak i dokładnie jeden znak na liczbę. Dane unikodu nigdy nie są dwuznaczne.
7-bitowy ASCII przechowuje wszystkie angielskie znaki za pomocą liczb z zakresu od 0 do 127. (65 jest wielką literą "A", 97 jest małą literą "a" itd.) Język angielski posiada bardzo prosty alfabet, więc może się całkowicie zmieścić w 7-bitowym ASCII. Języki zachodniej Europy jak język francuski, hiszpański, czy też niemiecki, korzystają z systemu kodowania nazwanego ISO-8859-1 (inne określenie to "latin-1"), które korzysta z 7-bitowych znaków ASCII dla liczb od 0 do 127, ale rozszerza zakres 128-255 dla znaków typu "ñ" (241), czy "ü" (252). Także unikod wykorzystuje te same znaki jak 7-bitowy ASCII dla zakresu od 0 do 127, a także te same znaki jak ISO-8859-1 w zakresie od 128 do 255, a potem w zakresie od 256 do 65535 rozszerza znaki do innych języków.
Kiedy korzystamy z danych w postaci unikodu, może zajść potrzeba przekonwertowania danych na jakiś inny system kodowania np. gdy potrzebujemy współdziałać z innym komputerowym systemem, a który oczekuje danych w określonym 1-bajtowym systemie kodowania, czy też wysłać dane na terminal, który nie obsługuje unikodu, czy też do drukarki.
I po tej notatce, powróćmy do Pythona.
>>> ord(u"ą") 261 #(1) >>> print unichr(378) #(2) ź
- W unikodzie polski znak "ą" jednoznacznie odpowiada cyfrze 261.
- Za pomocą funkcji
unichr, dowiadujemy się jakiemu znakowi odpowiada dana liczba. Liczbie 378 odpowiada polska litera "ź". Python automatycznie zakoduje wypisywany napis unikodowy, aby został poprawnie wyświetlony na naszym systemie.
Dlaczego warto korzystać z unikodu? Jest kilka powodów:
- Unikod bardzo dobrze sobie radzi z różnymi międzynarodowymi znakami.
- Reprezentacja unikodowa jest jednoznaczna; jednej liczbie odpowiada dokładnie jeden znak.
- Nie musimy się zamartwiać szczegółami technicznymi np. czy dwa łańcuchy, które ze sobą łączymy są w takim samym systemie kodowania [3].
- Python potrafi właściwie zinterpretować wszystkie znaki (np. co jest literą, co jest białym znakiem, a co jest cyfrą).
- Korzystając z unikodu zapobiegamy wielu problemom.
Dlatego wszędzie, gdzie będziemy korzystali z polskich znaków, będziemy korzystali z unikodu.
[edytuj] Materiały dodatkowe
- PEP 0263 wyjaśnia, w jaki sposób skonfigurować kodowanie kodu źródłowego.
Przypisy
- ↑ W tym podręczniku będziemy korzystać z kodowania UTF-8
- ↑ Niestety ciągle jest to nadmiernym uproszczeniem. Unikod został rozszerzony, aby obsługiwać teksty w starożytnych odmianach chińskiego, koreańskiego, czy japońskiego, ale one mają tak dużo znaków, że 2-bajtowy system nie może reprezentować ich wszystkich.
- ↑ W szczególności może się to zrobić niewygodne, kiedy korzystamy tylko ze standardowych łańcuchów znaków, a współpracujące ze sobą moduły korzystają z różnych systemów kodowania np. jedne z ISO 8859-2, a inne z UTF-8.