Kodowanie znaków: ASCII, UNICODE, UTF – co to znaczy?

Ucząc się programowania prędzej czy później natkniecie się dziwne krzaczki, które naprowadzą was na nowe pojęcie – kodowanie znaków.
Co nas dzisiaj czeka?

Zaczniemy od definicji – co to jest kodowanie znaków?

Kodowanie tekstu – przyporządkowanie znakom jednego alfabetu ciągów znaków innego alfabetu. Zestawy znaków oraz odpowiadający im kod binarny lub liczbowy.

System binarny

Musimy zacząć od podstaw, a te cofają nas do samego działania pamięci komputera. Komputer przechowuje informacje za pomocą zer i jedynek. Taki sposób nazywamy systemem binarnym lub dwójkowym. Podstawowymi pojęciami związanymi z tym systemem są bit oraz bajt.

Bit – jest najmniejszą jednostką w świecie cyfrowym, odpowiada stanowi komórki pamięci. Bit przyjmuje wartość zero lub jeden.

Ciąg ośmiu bitów (czyli zer i / lub jedynek) nazywamy bajtem (byte).
1 BAJT = 8 BITÓW
kodowanie znaków bajt - 8 bitów

Bajt daje aż 256 różnych kombinacji bitów. Stąd pozwala na zapisanie binarnie liczb od 0 do 255. Innymi słowy każdy bajt to liczba w przedziale od 0 do 255, ale zapisana za pomocą zer i jedynek np. 00000000 to liczba zero, 00000001 to jeden. Dwa natomiast zapiszemy jako 00000010. Dlaczego? Stan bitów odczytujemy od prawej do lewej, a kolejne pozycje odpowiadają, kolejnym potęgom 2. Spójrzcie na przykład:

zamiana z systemu dwójkowego na dziesiętny

A litery?
Liczby zapisane binarnie mogą też być interpretowane jako litery. Przykładowo 0100 0001 odpowiada 65 w systemie dziesiątkowym, co komputer zinterpretuje i wyświetli jako duże A.

W Pythonie możemy wykorzystać metodę chr()

>>> chr(65)
'A'
>>> chr(66)
'B'
>>> chr(97)
'a'
>>> chr(98)
'a'

oraz metodę ord(), aby poznać liczę odpowiadającą literze

>>> ord('A')
65
>>> ord('a')
97

Podobnie w innych językach np. w ruby

irb > 65.chr
'A'
irb > 'a'.ord
97

czy podobne operacje w JavaScripcie – charCodeAt i fromCharCode.

> 'A'.charCodeAt(0);
65
> String.fromCharCode(97,98,99);
'abc'

Wszystko to do tej pory wygląda dobrze.

Pytanie tylko skąd wiadomo jaka liczba odpowiada danej literze?

Standardy kodowania znaków

Aby pomóc w interpretacji liczb binarnych stworzono szyfry podstawieniowe zwane standardami. Istnieje kilka standardów kodowania znaków. Być może słyszeliście o UNICODE, ale wcześniej było jeszcze kilka innych.

ASCII

Pierwszym szeroko rozpowszechnionym standardem kodowania był ASCII. Pojawił się w 1960 za sprawą American National Standards Institute (ANSI), a jego korzenie sięgają kodu telegraficznego.

Tablica znaków ASCII to tabela składająca się z 127 znaków (7-bitowy zapis), w tym dużych i małych liter oraz znaków specjalnych jak np. spacja. Chcąc się odwołać do danego znaku należało podać jego numer w tabeli np. „A” to znany nam już numer 65, co daje w systemie binarnym 100 0001.

Z czasem okazało się, że ASCII nie jest doskonałe. Stworzone na bazie języka angielskiego nie uwzględniało znaków używanych w innych językach, dlatego w ASCII niemożliwe jest kodowanie polskich znaków dialektycznych czy znaków języka chińskiego.

Standard ASCII stał się jednak wzorcem dla kolejnych standardów. Możecie zauważyć, że pierwsze 127 znaków jest spójne dla pozostałych standardów w tym wpisie, stąd w literaturze noszą miano rozszerzeń ASCII.

Tablica ASCII
– kolejno w systemie dziesiętnym, szesnastkowym, dwójkowym, ósemkowym i kodowany znak
ASCII tablica kody liter

Tabela znaków: pierwsza część zawiera głównie znaki specjalne. Dalej widzimy przyporządkowanie kod ASCII – litera np. A – w systemie dziesiętnym: 65, szesnastkowo: 41, binarne: 100 0001.

Tablica kodów ASCII

Kod dziesiętny

Znak

Kod dziesiętny

Znak

Kod dziesiętny

Znak

Kod dziesiętny

Znak

0

NUL

32

Space

64

@

96

`

1

SOH

33

!

65

A

97

a

2

STX

34

66

B

98

b

3

ETX

35

#

67

C

99

c

4

EOT

36

$

68

D

100

d

5

ENQ

37

%

69

E

101

e

6

ACK

38

&

70

F

102

f

7

BEL

39

71

G

103

g

8

BS

40

(

72

H

104

h

9

TAB

41

)

73

I

105

i

10

LF

42

*

74

J

106

j

11

VT

43

+

75

K

107

k

12

FF

44

,

76

L

108

l

13

CR

45

77

M

109

m

14

SO

46

.

78

N

110

n

15

SI

47

/

79

O

111

o

16

DLE

48

0

80

P

112

p

17

DC1

49

1

81

Q

113

q

18

DC2

50

2

82

R

114

r

19

DC3

51

3

83

S

115

s

20

DC4

52

4

84

T

116

t

21

NAK

53

5

85

U

117

u

22

SYN

54

6

86

V

118

v

23

ETB

55

7

87

W

119

w

24

CAN

56

8

88

X

120

x

25

EM

57

9

89

Y

121

y

26

SUB

58

:

90

Z

122

z

27

ESC

59

;

91

[

123

{

28

FS

60

<

92

\

124

|

29

GS

61

=

93

]

125

}

30

RS

62

>

94

^

126

~

31

US

63

?

95

_

127

DEL

Jak czytać znaki ASCII na „nasze”?

Wartość 13 ma przyporządkowany CR (carriage return), inaczej znak end czy EOL (end of line) najprościej to kod ASCII enter – bin: 000 1101.
Wartości 9 odpowiada ASCII tab (tabulacja) – bin: 000 1001.
W ASCII znak and (&) ma przyporządkowaną wartość 38 – bin: 010 0110.

ANSI i ISO

Podjęto próbę rozwiązania problemu z zapisem znaków diakrytycznych występujących w innych językach. Tabela ASCII została rozszerzona do 256 znaków, co było możliwe dzięki wykorzystaniu 8-bitowego systemu zapisu liczb zamiast tylko 7 bitów (Window-1250 od Microsoftu i norma ISO-8859 stworzona przez ECMA).

Mimo to 256 było nadal zbyt małą liczbą, by uwzględnić wszystkie znaki specjalne, jakie istnieją we wszystkich językach, dlatego Microsoft zaproponował osobne wersje tabel ze znakami dla różnych grup językowych zwane stronami kodowymi (ang. ANSI code page). Jak wspomniałam, pierwsze 127 znaków bazuje na ASCII, reszta to znaki potrzebne do zapisu języka, którego dotyczy dana strona kodowa.

Strony kodowe mają przypisane specjalne numery np. Windows-1250 standard środkowoeuropejski, Windows-1251 odpowiada cyrylicy, Windows-1252 to standard zachodnioeuropejski, Windows-1253 koduje greckie znaki, Windows-1256 arabskie.

kodowanie w różnych standardach

Ciekawostka: Windows-1252 jest prawdopodobnie najczęściej używanym 8-bitowym kodowaniem znaków na świecie. We wrześniu 2019 r. 0,6% wszystkich stron internetowych zadeklarowało korzystanie z systemu Windows-1252, ale jednocześnie 2,9% korzystało z ISO 8859-1 (0,6% z top 1000 stron www), które według standardu HTML5 jest tym samym kodowaniem. Oznacza to, że na dzień dzisiejszy 3,5% stron wciąż korzysta z Windows-1252 ANSI encoding.

Wydawało się, że wystarczy wybrać odpowiedni język i problem z kodowaniem znaków znika.

Tylko co zrobić w przypadku, gdy w tekście pisanym w jednym języku pojawia się potrzeba wstawienia symbolu z innego? Np. w tekście w języku angielskim chcemy zamieścić specjalne litery takie jak znaki chińskie i greckie.

Kolejnym problemem było istnienie wielu równoległy standardów. Odpowiednikiem stron kodowych ANSI (tylko nazwa, nie są normowane przez ANSI) był wspomniany standard ISO 8859, częściowo kompatybilny z ANSI, jednak wciąż zapis w jednym systemie, mógł powodować błędy odczytu w drugim.

Z ilu liter składa się polski alfabet?

Jeśli się przyjrzymy, podstawowy alfabet angielski zawiera 26 liter (ISO basic latin). Język polski korzysta z 32 liter i zawiera te same litery co angielski (za wyjątkiem Q, V, X) oraz 9 znaków dialektycznych.

Kodowanie polskich znaków znajduje się na stronie kodowej Windows-1250 lub jako cp-1250 razem np. z czeskim, słowackim czy węgierskim. Odpowiednikiem Windows-1250 w normie ISO jest ISO 8859-2. Niestety nie są one w 100% kompatybilne. Zresztą zobaczcie sami.

kodowanie polskich znaków

Kodowanie polskich liter ze znakami diakrytycznymi w standardach Windows-1250 oraz ISO 8859-2:

StandardĄĆĘŁŃÓŚŹŻ
Windows-1250165198202163209211140143175
ISO-8859-2161198202163209211166172175
Standardąćęłńóśźż
Windows-1250185230234179241243156159191
ISO-8859-2177230234179241243182188191

Jak widać strony językowe nie rozwiązały wszystkich problemów, chociaż zbliżyły nas do rozwiązania.

UNICODE

Tak docieramy do UNICODE – The Unicode Standard.

Historia pokazała, że potrzebny będzie jeden, spójny system kodowania znaków, który będzie zawierać wszystkie możliwe symbole istniejące w językach świata. Powstała kolejna tabela, kolejny standard – UNICODE.

Koncepcja Unicode pojawiła się 1988, ale prace nad oficjalną publikacją trwały 3 lata. Unicode na początku składał się z 7161 znaków (w tym 127 to tabela znaków ASCII), natomiast najnowsza wersja z maja 2019 to repozytorium aż 137994 znaków. Nie jest to bynajmniej zamknięta grupa, liczba znaków nadal rośnie.

tablica Unicode

Mały fragment z tablicy Unicode, w którym pokazana jest polska litera „ą”
ma przypisaną wartość liczbową 0×105 (U+0105).

Mówiliśmy wcześniej, że 8 bitów pozwala na 256 kombinacji, czyli za pomocą 1 bajtu można zakodować 256 znaków.

W jaki sposób można zatem zapisać 137994 znaków UNICODE?
Wystarczy użyć większej ilości bajtów. Istnieje kilka sposobów kodowania tekstu, czyli konwersji wartości liczbowej z tablicy Unicode do postaci bitowej. Są to kodowania UTF: UTF-8, UTF-16 i UTF-32.

UTF-8

W UTF-8 znaki nie mają stałej długości bitów, przyjmują od 1 do 6 bajtów. Kodowanie UTF-8 jest kompatybilne z ASCII – pierwsze znaki Unicode, czyli 127 znaków tabeli ASCII koduje się jedno-bajtowo. Reszta jest zapisywana kolejno dwu, trzema, czterema, pięcioma i sześcioma bajtami. UTF-8 jest najbardziej ekonomicznym i najpopularniejszym ze sposobów zapisu wartości liczbowej z tablicy Unicode do postaci bitowej.

W UTF-8 polskie znaki dialektyczne są kodowane przez 2 bajty

Litera

Kod

heksadecymalny

dziesiętny

binarny

ą

0105

261

00000001 00000101

ć

0107

263

00000001 00000111

ę

0119

281

00000001 00011001

ł

0142

322

00000001 01000010

ń

0144

324

00000001 01000100

ó

00F3

243

00000000 11110011

ś

015B

347

00000001 01011011

ź

017A

378

00000001 01111010

ż

017C

380

00000001 01111100

Ą

0104

260

00000001 00000100

Ć

0106

262

00000001 00000110

Ę

0118

280

00000001 00011000

Ł

0141

321

00000001 01000001

Ń

0143

323

00000001 01000011

Ó

00D3

211

00000000 11010011

Ś

015A

346

00000001 01011010

Ź

0179

377

00000001 01111001

Ż

017B

379

00000001 01111011

Pojawia się też cała tablica znaków specjalnych np. „różnorodne symbole” Unicode zajmują pozycje U+2600—26FF (będą zapisywane różnie(!) w zależności od kodowania – utf-8, utf-16 czy utf-32) np. ☂, ☔, ♥, ♡, ☸ (o kubernetes! – żarcik 😛 )
tabela znaków specjalnych

Skąd się tu wzięły emoji?
Znaki graficzne, ideogramy zwane emoji pojawiły się poraz pierwszy w Japonii. Dzięki rozpowszechnieniu w social mediach, pierwsze emoji zostały wprowadzone w 2010 w wersji Unicode 6.0 i były oznaczone kodami U+1F600—1F64F. Obecnie są różnie wspierne i mogą się różnić wyglądem w zależności od miejsca wyświetlania (urządzenia, przeglądarki, czy aplikacji – fb, twitter etc).

Emoji raw vs w kolorze
emoji unicode utf-8

unicode znaki emoji utf-8

emojiunicode nrhexdecbin
😘U+1F618F0 9F 98 98403699112811110000 10011111 10011000 10011000
👁U+1F441F0 9F 91 81403698931311110000 10011111 10010001 10000001
🌸U+1F338F0 9F 8C B8403698808811110000 10011111 10001100 10111000
🦄U+1F984F0 9F A6 84403699469211110000 10011111 10100110 10000100

UTF-16

Znaki w UTF-16 przyjmują 2 bajty lub 4 bajty. Pierwsza część tabeli Unicode jest kodowana właśnie przy użyciu 2 bajtów, a następna za pomocą 4 bajtów. UTF-16 podobnie jak UTF-8 ma zmienną długość symbolu, ale jest jednak mniej skomplikowany, dlatego można go nazwać kompromisem pomiędzy UTF-8 a UTF-32. Używany najczęściej przy kodowaniu języków azjatyckich.

UTF-32

UTF-32 wykorzystuje stałą, 32-bitową długość czyli 4 bajty dla każdego znaku. Jego wadą jest zużycie dużej ilości pamięci, gdyż każdy znak ma zawsze cztery bajty.

Duża litera A czyli 65 miejsce w tabeli otrzymuje zapis 00000000 00000000 00000000 0100 0001 w systemie binarnym. Dla porównania w UTF-8 zapis wyglądałby tak: 0100 0001.
kodowanie UTF-8 vs UTF-32
W przypadku najbardziej popularnych znaków (np. pierwsze 127 znaków ASCII) marnowane są bajty, bo przynajmniej połowa bitów pozostaje zerowa. Niepotrzebnie wydłuża się wielkość zapisu znaków. Z drugiej strony każdy znak ma taką samą długość 4 bajtów, co ułatwia znalezienie danego symbolu w ciągu. Ze względu na długość UTF-32 jest mało popularnym systemem stosowanym zwykle w pamięci operacyjnej.


UWAGA: Mała prywata!

Lubisz FlyNerda? Wpadasz tu czasem na posty takie jak przewodniki po front-endzie czy wymaganiach junior Python developera?

Wkrótce moje urodziny, z tej okazji możecie zrobić mi prezent i Alexowi.

Alex urodził się w czerwcu, jest synem moich dobrych znajomych, sąsiadów, którzy do tego w życiu zawsze byli dla mnie wzorem (Magda jest onkologiem dziecięcym, Paweł pracuje w bliskiej mi organizacji – stowarzyszeniu Otwarte Klatki). Zostałam ciocią!

W dniu, kiedy Alex skończył dokładnie pięć tygodni, zdiagnozowano u niego rdzeniowy zanik mięśni typu I (SMA I), chorobą do niedawna właściwie beznadziejną. Jednak dzięki szybkiej reakcji rodziców otrzymuje już lek, teraz konieczna jest stała rehabilitacja. Oprócz tego czekamy na kosztowną, ale skuteczną terapię genową.

Możecie wyrazić sympatię do mnie, dając mi „urodzinowy prezent”, robiąc coś dobrego dorzucając się do zbiórki dla Alexa! ❤️

Każdy teraz dorzuca się do zbiórki na moje urodzin 🎂 (3 października). Zbieram dla Alexa – synka znajomych, u którego…

Posted by FlyNerd.pl – blog o programowaniu on Thursday, September 26, 2019