Це велика тема! Про неї можна написати цілу книжку, і кілька людей так і зробили (пошукайте на Amazon «unicode book», щоб побачити приклади).
Робота із символами в компʼютерах була значно простішою в попередні десятиліття, коли програмісти вважали, що англійська є єдиною важливою мовою. Отже: 26 літер у верхньому і нижньому регістрі, 10 цифр, кілька розділових знаків, плюс код (0x07), щоб продзвонити в дзвіночок, і все це вміщалося в 7 бітів: набір символів ASCII.
Звісно, люди почали питати, а як же à, ä та Ł, потім інші почали питати про ऄ, ஹ і ญ, а молодь захотіла емодзі 😱. Що робити?
Якщо коротко, багатьом розумним і терплячим людям довелося роками засідати в комітетах, опрацьовуючи деталі набору символів Unicode та кодувань на кшталт UTF-8, а багато програмного забезпечення потребувало дуже складної переробки. До того ж зʼявилося багато нових багів.
Щоб усе не зламалося, дизайн Unicode/UTF-8 гарантує, що перші 127 кодів збігаються з ASCII (навіть дзвіночок).
Мови, розроблені після приблизно 2005 року, мають величезну перевагу: досить стабільний стандарт Unicode уже існував.
Julia (уперше випущена 2012 року) могла припускати, що все буде Unicode: символи, рядки тексту (англ. string), імена змінних і функцій, математичні оператори...
Як сказано в документації, «Julia робить роботу зі звичайним ASCII-текстом простою та ефективною, а підтримка Unicode є настільки простою й ефективною, наскільки це можливо». Зверніть увагу на «наскільки це можливо»: це важлива частина твердження.
Символьні літерали записуються в одинарних лапках і відрізняються від рядків тексту, записаних у подвійних лапках.
Це очевидно для людей зі світу C/C++, але може заплутати програмістів Python і JavaScript.
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)
З прикладів видно, що тип - Char, і Julia має додаткову інформацію про категорію символу.
Придивившись уважніше, бачимо, що ці символи можна подати 4 шістнадцятковими цифрами. А повний набір символів потребує до 6 шістнадцяткових цифр.
Ці числа називають «кодовими точками», і наразі вони охоплюють діапазон від U+0000 до U+10FFFF.
Вони показуються в REPL, але в коді для їх отримання використовуйте codepoints().
Перетворити Char на Int і навпаки дуже просто:
julia> Int('a')
97
julia> Char(97)
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
Компілятор дозволяє деякі форми цілочисельної арифметики над Char:
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)
Багато функцій для роботи з рядками тексту можуть також працювати з вхідними даними типу Char.
uppercase() та lowercase().isuppercase() та islowercase().isletter() охоплює багато алфавітівisdigit() перевіряє строго 0:9isnumeric() ширша за isdigit, тож дає true для ¾ і різних неєвропейських письменностейisxdigit() для шістнадцяткових цифрisascii() для символів до Unicodeispunct() для розділових знаківisspace() для будь-якого пробільного символуisprint() для друкованих символів (протилежна до неї - iscntrl())islowercase('A') # false
uppercase('γ') # 'Γ': Unicode U+0393 (category Lu: Letter, uppercase)
ispunct('@') # true
isdigit('A') # false
isxdigit('A') # true
Щоб перетворити рядок тексту на вектор Char, можна скористатися collect().
А щоб перетворити вектор Char на рядок тексту, є конструктор String().
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"
Це працює з будь-якими символами, не лише з ASCII.
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)
Зауважте, що конструктор String() працює з вектором.
Щоб привести один Char до рядка тексту з одного символу, використовують функцію string(), з малої s.
julia> string('a')
"a"
Усе, про що йшлося в цьому документі досі, здається відносно простим, тож невже справді немає про що хвилюватися?
На жаль, це надто оптимістично!
Одна зі складнощів походить з потреби в «до» 6 шістнадцяткових цифрах на кодову точку. Це означає, що різним символам потрібен різний обсяг памʼяті за кодування UTF-8.
Байт може зберігати лише (беззнакові) числа до 255, тобто дві шістнадцяткові цифри, тож UTF-8 використовує змінну кількість байтів (від 1 до 4), щоб зберегти Char.
Їх називають «кодовими одиницями», і функція ncodeunits() поверне кількість, потрібну для заданого символу.
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
Крім того, не все, що можна показати на екрані, має власну унікальну кодову точку. Деякі візуально різні символи вважаються похідними від інших, тож Unicode розглядає їх як батьківський символ плюс модифікатор.
Ця проблема стосується рядків тексту, де вона створює труднощі з індексацією.
У цій вправі ми реалізуємо частковий набір допоміжних функцій, які допоможуть розробнику впорядковувати назви ідентифікаторів.
У цих 6 завданнях ми поступово побудуємо функції transform для перетворення окремих символів і clean для перетворення рядків тексту (англ. string).
Правильний ідентифікатор складається з нуля або більше літер, підкреслень, дефісів, знаків питання та емодзі.
Якщо до функції clean передати порожній рядок тексту, вона має повернути порожній рядок тексту.
Реалізуйте функцію transform, щоб замінювати будь-які дефіси на підкреслення.
julia> transform('-')
"_"
Видаліть усі пробільні символи. Це стосується пробільних символів на початку й у кінці.
julia> transform(' ')
""
Змініть функцію transform, щоб вона перетворювала camelCase на kebab-case
julia> transform('D')
"-d"
Змініть функцію transform, щоб вона пропускала будь-які символи, які є цифрами.
julia> transform('7')
""
Змініть функцію transform, щоб вона замінювала будь-які грецькі літери в діапазоні від 'α' до 'ω'.
julia> transform('β')
"?"
Реалізуйте функцію clean, щоб застосовувати ці операції до всього рядка тексту.
Символи, які не підпадають під ці правила, мають залишатися без змін.
julia> clean(" a2b Cd-ω😀 ")
"ab-cd_?😀"
Зареєструйтеся на Exercism, щоб вивчати й опановувати Julia, а також 35 концепцій128 вправ та справжнє наставництво від людей, і все це безкоштовно.