Unicode

Slova/znakovi su predstavljeni u memoriji pomoću brojeva. To pridruživanje se naziva kodiranje karaktera ili kodna stranica. Primjer je ASCII kôd koji definira kodove od 0-127 (npr. slovo ‘A’ ima kôd 65, slovo ‘B’ 66, itd.)

Slova s kvačicama nisu na tom popisu. Kroz povijest su se slovima s kvačicama dodjeljivali kodovi na više načina:

  1. umjesto nekih znakova ASCII kôda (0-127), npr. ČĆĐŠŽ umjesto ^]\[@, a čćđšž umjesto ~}|{\. (postoje i druge nacionalne varijante http://en.wikipedia.org/wiki/ISO/IEC_646)
  2. umjesto nekih znakova proširenog ASCII kôda (128-255). (vidi http://en.wikipedia.org/wiki/ISO/IEC_8859)
  3. unicode: kôd duljine 4 bytea pridružen svakom znaku/slovu iz bilo kojeg pisma (http://en.wikipedia.org/wiki/ISO_10646)

Mana ovog prvog pristupa je da se nije moglo u istom dokumentu imati istovremeno znakove kao \, [, { i slova s kvačicama.

Situaciju donekle ispravlja drugi pristup u kojem također programi moraju znati koji skup kodova (kodna stranica) se koristi.

Treći način (unicode) omogućuje da se za sva pisma koristi ista kodna stranica. Međutim i tu postoji više varijanti zapisa. Razlikuju se po tome koliko se byteova koristi za zapis, kojim redoslijedom, itd., pa postoje tzv.: UCS-2, UCS-4, UTF-16, UTF-32, UTF-8, ...

Trenutno je najpopularniji način kodiranja UTF-8, koji koristi 1-6 byteova za kodiranje jednog znaka (broj byteova varira od znaka do znaka), a unazad je kompatibilan s ASCII standardom (to znači da je svaka ASCII tekst datoteka ujedno i UTF-8 datoteka). Također nikad ne koristi byte nula, tako da se neće nikad umjetno pojaviti null-terminator, pa će stoga funkcije iz C-a kao strcpy() raditi ispravno. Također, vrijednosti kodova 0-127 se ne koriste za znakove koji zahtijevaju višebyteni zapis što znači da se npr. znak za novi red ne može slučajno pojaviti a to znači da editor koji ne zna da se radi o UTF-8 može točno odrediti koliko ima redaka teksta.

To su sve dobra svojstva u odnosu na ostale načine kodiranja, iako treba biti svjestan da prikaz znaka u UTF-8 (i u ostalim unicode kodiranjima) nije jednoznačan. Npr. slovo č posjeduje vlastiti kôd (NFC normalizacija), ali se č može prikazati kao kôd za c + kôd za kvačicu (NFD normalizacija). Više o normalizacijama na http://stackoverflow.com/a/7934397, http://www.unicode.org/reports/tr15. Dodatna komplikacija je da postoje i nizovi znakova koji kao niz imaju svoj kôd (npr. tri točke), pa neki načini normalizacije mogu biti ireverzibilni.

Više na:

Da bi se omogućio rad s UTF-8 u pythonu potrebno je:

  1. u editoru to odabrati kao opciju.
  2. u samim python programima dodati liniju koja označava da je kodiranje koje se koristi UTF-8

U nekim slučajevima je korisno da python interno vodi neki string kao unicode. To se može postići prefixom u ispred navodnika.

# -*- coding: utf-8 -*-

a = "č"
print a
print repr(a)

a = u"č"
print a
print repr(a)
č 
'\xc4\x8d'
č 
u'\u010d'

Ako imamo string koji nije interno prikazan kao unicode možemo ga dekodirati iz njegovog kôda u unicode pomoću string.decode().

# -*- coding: utf-8 -*-

a = "č"
print repr(a.decode("utf8"))
u'\u010d'

Ako imamo string koji jest interno prikazan kao unicode, možemo ga kodirati u neki drugi kôd pomoću string.encode().

# -*- coding: utf-8 -*-
b = u"č"
print repr(b.encode('utf-8'))
print repr(b.encode('ISO-8859-2'))
print repr(b.encode('ISO-8859-16'))
print repr(b.encode('cp852'))
print repr(b.encode('cp1250'))
print repr(b.encode('UTF-32'))
print repr(b.encode('UTF-16'))
'\xc4\x8d'
'\xe8'
'\xb9'
'\x9f'
'\xe8'
'\xff\xfe\x00\x00\r\x01\x00\x00'
'\xff\xfe\r\x01'

Popis kodiranja koje python podržava: https://docs.python.org/2/library/codecs.html#standard-encodings

Na ovaj način, koristeći prvo decode, a zatim encode, možemo promijeniti kodiranje neke tekstualne datoteke. Za tu svrhu postoje i gotovi programi (npr. recode) (http://stackoverflow.com/questions/64860/best-way-to-convert-text-files-between-character-sets).

Modul unicodedata nam može dati podatke o pojedinom unicode znaku:

# -*- coding: utf-8 -*-
import unicodedata

a = u"čćžšđ"

for c in a:
  print '%04x' % ord(c), "%10s" % repr(c), unicodedata.category(c), unicodedata.name(c)
010d  u'\u010d' Ll LATIN SMALL LETTER C WITH CARON
0107  u'\u0107' Ll LATIN SMALL LETTER C WITH ACUTE
017e  u'\u017e' Ll LATIN SMALL LETTER Z WITH CARON
0161  u'\u0161' Ll LATIN SMALL LETTER S WITH CARON
0111  u'\u0111' Ll LATIN SMALL LETTER D WITH STROKE

Promjenom normalizacije možemo prebaciti prikaz npr. sa č na c + kvačica:

# -*- coding: utf-8 -*-
import unicodedata

a = u"č"
b = unicodedata.normalize("NFD",a)

print repr(a)
for c in a:
  print '%04x' % ord(c), "%10s" % repr(c), unicodedata.category(c), unicodedata.name(c)

print repr(b)
for c in b:
  print '%04x' % ord(c), "%10s" % repr(c), unicodedata.category(c), unicodedata.name(c)
u'\u010d'
010d  u'\u010d' Ll LATIN SMALL LETTER C WITH CARON
u'c\u030c'
0063       u'c' Ll LATIN SMALL LETTER C
030c  u'\u030c' Mn COMBINING CARON

Na sličan način možemo prebaciti npr. znak № u slova od kojih se on sastoji N i o:

# -*- coding: utf-8 -*-
import unicodedata

a = u"№"
b = unicodedata.normalize("NFKC",a)

print repr(a)
for c in a:
  print '%04x' % ord(c), "%10s" % repr(c), unicodedata.category(c), unicodedata.name(c)

print repr(b)
for c in b:
  print '%04x' % ord(c), "%10s" % repr(c), unicodedata.category(c), unicodedata.name(c)
u'\u2116'
2116  u'\u2116' So NUMERO SIGN
u'No'
004e       u'N' Lu LATIN CAPITAL LETTER N
006f       u'o' Ll LATIN SMALL LETTER O