Ez egy nagy téma! Lehetne róla egy hosszú könyvet írni, és többen meg is tették (keress rá az Amazonon az „unicode book” kifejezésre, hogy láss néhány példát).
A karakterek kezelése a számítógépekben sokkal egyszerűbb volt a korábbi évtizedekben, amikor a programozók azt feltételezték, hogy az angol az egyetlen fontos nyelv. Szóval: 26 betű, kis- és nagybetű, 10 számjegy, néhány írásjel, plusz egy kód (0x07) a csengő megszólaltatásához, és mindez elfért 7 bitben: ez az ASCII karakterkészlet.
Természetesen az emberek elkezdték kérdezni, mi a helyzet az à, ä és Ł karakterekkel, aztán mások az ऄ, ஹ és ญ jelekről kezdtek kérdezni, a fiatalok pedig emojikat akartak 😱. Mit tegyünk?
Hogy rövidre fogjuk: sok okos és türelmes embernek évekig bizottságokban kellett ülnie, hogy kidolgozza a Unicode karakterkészlet és az olyan kódolások, mint a UTF-8 részleteit, és rengeteg szoftvert kellett nagyon bonyolultan újraírni. Ráadásul rengeteg új bug is bekerült.
Hogy ne minden törjön el, a Unicode/UTF-8 kialakítása gondoskodik róla, hogy az első 127 kód megegyezzen az ASCII-vel (a csengőt is beleértve).
A nagyjából 2005 után tervezett nyelveknek hatalmas előnyük, hogy már létezett egy viszonylag stabil Unicode-szabvány.
A Julia (első kiadása 2012-ben) abból indulhatott ki, hogy minden Unicode lesz: a karakterek, a stringek, a változó- és függvénynevek, a matematikai operátorok...
A kézikönyvet idézve: „A Julia egyszerűen és hatékonyan kezeli a sima ASCII-szöveget, a Unicode kezelése pedig a lehető legegyszerűbb és leghatékonyabb.” Figyeld meg a „a lehető” részt, ez a megfogalmazás fontos eleme.
A karakterliterálokat aposztrófok közé írjuk, és megkülönböztetjük őket az idézőjelek közé írt stringektől.
Ez nyilvánvaló a C/C++ világából érkezőknek, a Python és Javascript programozóknak viszont zavaró lehet.
julia> a = 'a' # Roman alphabet
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
julia> typeof(a)
Char
julia> jha = 'झ' # Devanagari alphabet
'झ': Unicode U+091D (category Lo: Letter, other)
julia> typeof(jha)
Char
julia> '❤' # heart emoji
'❤': Unicode U+2764 (category So: Symbol, other)
A példákból láthatjuk, hogy a típus Char, és a Julia további információkkal szolgál a karakter kategóriájáról.
Közelebbről megnézve úgy tűnik, hogy ezek a karakterek 4 hexadecimális számjeggyel ábrázolhatók. A teljes karakterkészlethez akár 6 hexadecimális jegy is kell.
Ezeket a számokat „kódpontoknak” nevezik, és jelenleg U+0000-tól U+10FFFF-ig terjednek.
A REPL-ben megjelennek, a kódban viszont a codepoints() függvénnyel kérheted le őket.
A Char és az Int közötti átalakítás egyszerű:
julia> Int('a')
97
julia> Char(97)
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
A fordító a Char értékeken az egészszám-aritmetika néhány formáját engedélyezi:
julia> 'b' - 'a' # interval, in alphabetic order
1
julia> 'b' + 'a'
ERROR: MethodError: no method matching +(::Char, ::Char)
julia> 'a' + 5
'f': ASCII/Unicode U+0066 (category Ll: Letter, lowercase)
julia> 'f' + ('A' - 'a') # same as `uppercase('f')`
'F': ASCII/Unicode U+0046 (category Lu: Letter, uppercase)
A stringkezelő függvények egy része Char bemeneten is működik.
uppercase() és a lowercase() függvénnyel.isuppercase() és az islowercase() függvénnyel.isletter(), sok ábécét lefedisdigit(), szigorúan a 0:9 tartományt vizsgáljaisnumeric(), tágabb, mint az isdigit, ezért true a ¾-re és több nem európai írásrendszerre isisxdigit(), hexadecimális számjegyekisascii(), Unicode előtti karakterispunct(), írásjelisspace(), bármilyen whitespace-karakterisprint(), nyomtatható karakterek (az ellentéte az iscntrl())islowercase('A') # false
uppercase('γ') # 'Γ': Unicode U+0393 (category Lu: Letter, uppercase)
ispunct('@') # true
isdigit('A') # false
isxdigit('A') # true
Annak ellenőrzésére, hogy egy karakter szerepel-e egy stringben, az in operátort használhatjuk.
Figyeld meg, hogy ez eltér a részstringek vizsgálatától:
# char search
julia> 'a' ∈ "xabcahliazlnkiw"
true
# substring search
julia> "a" ∈ "xabcahliazlnkiw"
use occursin(needle, haystack) for string containment
A reguláris kifejezések (amelyekről egy másik fogalom szól) szintén hatékony keresést és módosítást tesznek lehetővé.
Stringből Char-vektorba a collect() függvénnyel juthatunk.
Char-vektorból stringbe a String() konstruktorral.
julia> s = "abcde"
"abcde"
julia> cv = collect(s)
5-element Vector{Char}:
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase)
'e': ASCII/Unicode U+0065 (category Ll: Letter, lowercase)
julia> String(cv)
"abcde"
Ez bármilyen karakterrel működik, nem csak ASCII-vel.
julia> collect("❤,😱")
3-element Vector{Char}:
'❤': Unicode U+2764 (category So: Symbol, other)
',': ASCII/Unicode U+002C (category Po: Punctuation, other)
'😱': Unicode U+1F631 (category So: Symbol, other)
Figyeld meg, hogy a String() konstruktor egy vektoron működik.
Ahhoz, hogy egyetlen Char-t egykarakteres stringgé alakíts, a string() függvény kell, kisbetűs s-sel.
julia> string('a')
"a"
A dokumentumban eddig minden viszonylag egyszerűnek tűnik, szóval tényleg nincs miért aggódni?
Sajnos ez túl optimista!
Az egyik bonyodalom abból adódik, hogy kódpontonként „akár” 6 hexadecimális jegyre lehet szükség. Ez azt jelenti, hogy UTF-8 kódolás esetén a különböző karakterek különböző mennyiségű memóriát foglalnak.
Egy byte csak legfeljebb 255-ig, azaz két hexadecimális jegyig tud (előjel nélküli) számokat tárolni, ezért a UTF-8 változó számú byte-ot (1-4) használ egy Char tárolására.
Ezeket „kódegységeknek” nevezik, és az ncodeunits() függvény adja vissza, mennyi kell egy adott karakterhez.
julia> codepoint(jha) # jha 'झ' is defined in an earlier example
0x0000091d
julia> ncodeunits(jha)
3
julia> ncodeunits('a') # ASCII character
1
julia> ncodeunits('😱') # emoji
4
Ráadásul nem minden, a képernyőn megjeleníthető dolognak van saját egyedi kódpontja. Néhány vizuálisan elkülönülő karaktert másokból származtatottnak tekintenek, ezért a Unicode egy szülőkarakter és egy módosító kombinációjaként kezeli őket.