Кодування тексту з’ясуйте, як d; Юнікод - Бузут
Тепер, коли ми знаємо про необхідність Unicode та його історію, давайте розглянемо технічні деталі. Ми сказали, що консорціум стандартизує кілька кодувань, так що вони є і як вони працюють ?
Оскільки ці кодування дуже популярні, їх імена вам знайомі, тим більше, що ми вже згадували їх у попередніх розділах. Це кодування UTF-n. Різниця між цими кодуваннями - це мінімальна довжина байтів, необхідна для представлення символу.
Ми говорили, що спочатку Unicode - це все у два байти. Оскільки в більшості латинських мов використовується дуже мало знаків, що не належать до ASCII, для багатьох це неприйнятна втрата місця.
З іншого боку, для мов, що використовують знаки, які не є частиною ASCII, ці кодування дуже корисні. Тому потрібно було знайти рішення, яке підходило б усім. Отже, в Unicode кодові точки та кодування - це дві дуже різні речі.
Таблиця Unicode
Unicode - це перш за все гігантська таблиця, яка присвоює унікальний номер кожному символу, його кодовій точці або кодова точка. Таблиця Unicode дозволяє вам призначати 1114112 (2 16 x 17) кодових точок. Сьогодні нараховується лише 25% з цих кодових балів.
Ця таблиця розділена на 17 площин (від 0 до 16) по два байти в кожну, або 65 536 кодових точок на площину (65 536 х 17 = 1 114 114). Ці плани дозволяють легко визначати групи персонажів. Передній план, називається Базова багатомовна площина (BMP), або Базовий багатомовний план, об’єднує 65 тис. Найпоширеніших символів. Плани з 1 по 16 називаються додатковими планами.
Крім того, говорити про "кодування Unicode" насправді не має сенсу. Наприклад, в Unicode "привіт" перекладається як:
Це кодові коди Юнікоду, які відповідають різним літерам слова. Як бачите, кодові точки виражаються в шістнадцятковій системі. Хоча зазвичай це виражається таким чином, їх також можна представити через їх десятковий еквівалент.
HTML також дозволяє обидва позначення.
Ви можете перевірити це за допомогою кодера HTML. Різні знаки та пов’язані з ними кодові точки перелічені на сайті таблиці Unicode.
Цих кодових точок, виражених у шістнадцятковій, десятковій чи двійковій формі, недостатньо для кодування знака в його двійковому поданні.
Кодування
До цього часу у нас є лише один спосіб зіставити знак і його номер у таблиці Unicode. Це не говорить нам, як зберігати ці предмети в пам'яті. Саме тут надходять кодування.

Кодування фіксованого розміру
Перевага цих кодувань полягає в простоті синтаксичного аналізу та пошуку, а також у вирізанні, оскільки який би не був символ, його розмір відомий заздалегідь. Зіставлення двійкових даних з кодовою точкою полегшено. Однак це робиться за рахунок використовуваного простору (оперативної пам'яті, диска, пропускної здатності тощо).
У перші дні Unicode, як ми вже бачили, все зберігалося у двох байтах. Це кодування, хоча і застаріле, все ще існує, називається UCS-2 і має фіксовану довжину. Він не дозволяє представляти всі символи Unicode, а лише перші 65 тис., Які є найпоширенішими (однак без смайлів).
Отже, наш привіт можна зберігати в пам'яті наступним чином (подання у гекса).
Але, як ми вже згадували, на машині в мало-ендіанський, байти будуть перевернуті.
Приклад ілюструє непотрібне використання місця, оскільки все зберігається у двох байтах, якщо використовуються лише символи ASCII - і навіть для деяких латинських символів, таких як наголошені літери, витрачається по одному байту на символ.
Тепер, якщо ми подивимося на подання знака "€", два байти використовуються правильно.
З іншого боку, всі символи, що знаходяться поза межами BMP, тобто 65536 найпоширеніших символів, не можуть бути представлені в UCS-2. Ось чому це кодування сьогодні не є частиною стандарту Unicode.
UTF-32
Це кодування є найдовшим кодування фіксованого розміру за стандартом Unicode, і воно також є єдиний фіксованого розміру, який не амортизується. Він використовується для представлення всіх символів Unicode на 32 біти або чотири байти. Ми беремо наш попередній приклад і кодуємо його в UTF-32BE.
Тут втрачений простір просто вражає. Навіть смайли не займають стільки місця, ось піца 🍕 в UTF-32BE.
Перевага цього кодування полягає в тому, що всі символи мають однаковий розмір. Для цього він навряд чи коли-небудь використовується для зберігання тексту, а скоріше служить у внутрішніх API. Наприклад, Python 3 використовує його для представлення текстових змінних.
Кодування змінних розмірів
Сьогодні, два кодування змінного розміру відповідають стандарту Unicode, UTF-8 та UTF-16. Їх максимум - чотири байти, але мінімальний розмір знака - один і два байти відповідно.
Це, мабуть, найбільш широко вживане кодування сьогодні. Як для зберігання, так і для передачі інформації. Його мінімальний розмір - вісім біт, і він може досягати чотирьох байт для деяких знаків.
UTF-8 також має особливість бути цілком зворотно сумісна з ASCII. Будь-який дійсний символ ASCII є дійсним символом UTF-8. Крім того, оскільки воно складається з однобайтових слів, воно не є чутливим до ендіану.
Оскільки це кодування змінного розміру, деякі знаки складаються з декількох слів. Для того, щоб мати можливість визначити кількість слів, що складають символ, UTF-8 поміщає цю інформацію в перший байт.
- будь-яка кодова точка зі значенням менше 128 кодується в одному байті з найбільш значущим бітом при нулі (як у ASCII),
- інші кодові коди кодуються кількома байтами. Перший байт має найбільш значущі біти в 1 із стільки 1, скільки є слів. Отже, у нас є мінімум два і до чотирьох одиниць, за якими слідує 0, тоді як решта бітів використовуються для кодування інформації. Наступні слова повинні починатися з 10 .
| 1 | 0xxxxxxx |
| 2 | 110xxxxx 10xxxxxx |
| 3 | 1110xxxx 10xxxxxx 10xxxxxx |
| 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
Давайте подивимось це на кількох конкретних прикладах. Буква "е" кодується як у ASCII. Або 65hex, або 101dec, це дає 01100101bin. Поки що це дуже просто.
Тепер візьмемо смайлик 😎 як приклад. Якщо ми подивимося на таблицю Unicode, ми зрозуміємо, що її код U + 1F60E, або 128526dec. Однак, якщо ми подивимося на деталі кодування, як шестнадцатеричное, так і десяткове представлення не відповідають.
Щоб повернути свою цінність, ми повинні зрозуміти кодування, пояснене вище. Давайте подивимось це докладно.
UTF-16
UTF-16 має змінну довжину з 16-бітовими словами. Таким чином, в UTF-16 знак - це два або чотири байти. На відміну від UTF-8, UTF-16 не сумісний з ASCII - і, звичайно, не сумісний з UTF-8.
Однак, UTF-16 повністю сумісний із UCS-2, перше кодування Unicode, яке було зафіксовано в розмірі на два байти. Таким чином, будь-який допустимий знак в UCS-2 також є в UTF-16, і все, що може бути закодовано в UTF-16 на слові, тому є дійсним символом UCS-2.
На відміну від UCS-2, який обмежений BMP, UTF-16 дозволяє кодувати всі символи в таблиці Unicode. Перші 65536 кодуються як в UCS-2, їх значення в двох байтах, решта 1048 576 символів вимагають використання другого слова.
Кожного разу, коли вам потрібно закодувати символ, який не є частиною BMP, UTF-16 використовує те, що називається сурогатні пари або зони напівпрямості. Ці кодові точки є частиною BMP, але не є символами, зарезервовані для використання UTF-16 для кодування символів, що не є частиною BMP.
Таким чином, із 65 536 символів BMP 1024 зарезервовано для високих сурогатів, а ще 1024 зарезервовано для низьких сурогатів. 1024 x 1024 = 1048 576, рахунок хороший !
В UTF-16 будь-який символ, що складається з двох шістнадцять розрядних слів, є комбінацією високого сурогату для першого слова та низького сурогату для другого. За цими двома значеннями ми можемо обчислити представлений символ.
Крім того, оскільки використовувані символи є несимвольними, їх ніколи не можна використовувати для представлення чогось іншого. Крім того, ця особливість означає, що UTF-16, як і UTF-8, автоматично синхронізується, оскільки достатньо знайти сурогат, щоб знати, що це півслова, а якщо це - це початок чи кінець.
Кодування виконується так:
Іншими словами, щоб отримати значення кодової точки з кодування UTF-16, ми виконуємо наступну операцію 10000hex + (H - D800hex) × 400hex + (L - DC00hex) де H і L відносяться до сурогатних пар Високого та Низького.
Підтримка мов програмування
Багато мов використовували Unicode версії 1.0 і тому встановили, що символ має два байти. Коли виявилося, що в підсумку буде значно більше 65 тис. Символів, вони, природно, обрали UTF-16, щоб забезпечити зворотну сумісність. Це особливо стосується Java та JavaScript, тип String представлений внутрішньо в UTF-16.
Однак у JavaScript деякі методи, такі як довжина, працюють із кількістю слів UTF-16.
Це питання знання того, що слід рахувати: кодова точка? байт? графема? У JavaScript багато методів працюють безпосередньо з кількістю слів UTF-16. Тому негайно після виходу з БМП результат може здивувати.
Однак ітератор String працює безпосередньо з кодовими точками UTF-16. Отже, цей метод можна використовувати, якщо ви хочете працювати не над словами UTF-16, а безпосередньо над символами.
Інші мови не підтримують Unicode, але це не заважає їм бути сумісними. Вони просто не є Unicode обізнаний. Це, наприклад, випадок з PHP. У цій мові тип String еквівалентний одному байту. Таким чином, будь-який символ, кодований більше ніж одним байтом, PHP не розпізнає як такий.
PHP підраховує кількість байтів, крапка. Однак він зможе передавати відповідні байти, і вони можуть бути інтерпретовані відповідно пізніше (наприклад, у браузері). У PHP загальним кодуванням є кодування вихідного коду, яке повинно бути сумісним з ASCII. Проте можна змінити це за допомогою прапора Zend multibyte.
Навіть мов немає Unicode обізнаний переважно пропонують інструменти для роботи з Unicode. У PHP це функції mbstring, у JavaScript деякі методи, такі як codePointAt, fromCodePoint або прапор u регулярного виразу, дозволяють підтримувати Unicode.
Це не секрет, для роботи з Unicode потрібно знати свою мову та використовувати відповідні функції. Як би сказав JCVD, будьте в курсі!
До речі, якщо ви хочете дослідити відмінності в управлінні між мовами та побачити кілька підлих прикладів випадків, раджу прочитати цю дуже цікаву статтю: “Не неправильно, що“ 🤦🏼♂️ ”. Довжина == 7.
Застаріле кодування
Не вдаючись у подробиці, давайте швидко згадаємо кодування, які сьогодні застаріли, але які ви все ще можете зустріти в природі.
- UTF-1 є рекомендованим кодуванням у першому стандарті ISO. Це кодування змінного розміру від 1 до 5 байт. Він не включає механізм синхронізації, який робить виправлення помилок складним і повільним через використання множення/ділення на числа, що не мають рівня 2.
- UTF-7 - це кодування змінного розміру із 7-бітовими словами. Він пропонує представляти символи Unicode із потоком ASCII. Він був розроблений, щоб дозволити передачу Unicode із системами SMTP, які не є 8-бітний чистий, Тобто не підтримує текст, кодований понад 7 бітів. Це кодування уникає послідовностей Unicode і представляє їх за двійковим значенням, закодованим у base64.
Різні уявлення персонажа
Ми знаємо, що Unicode - це гігантська таблиця символів і що стандарт визначає декілька кодувань для того, щоб представити ці символи у двійковому вигляді. З різних причин, включаючи сумісність зі старими таблицями, один і той же символ може кодуватися по-різному.
Комбіновані символи
Чи знали ви, що багато персонажів можна представити у різних формах? Сюди входять усі символи, які можуть складатися з наголосів. Таким чином, наші é, è, ê, ë, à, ç і т. Д., Представлені тут як попередньо складені символи - одна кодова точка для кожного - також можуть бути складені, представлені двома послідовними кодовими точками.
У складеній формі спочатку є символ без його наголосу, потім другий символ представляє лише наголос, діакритичний знак, які мають повні кодові точки. Потім графема передається у „нормальній” формі.
Наприклад, ось "е", за яким слідує циркумфлекс: ê. Побачити різницю тут неможливо, однак, це "ê" справді складається з двох символів, e після яких ^, або в Unicode:
Ця можливість також дає можливість формувати символи певних мов, що складаються з декількох наголосів, тоді як останні не існують у своїй попередньо складеній формі (наприклад, "diakrī́nō", щоб розрізнити давньогрецькою етимологію слова діакритичний).
Якщо скопіювати ці символи в інструмент HTML-сутності, ви одразу побачите коди різних символів.
Повторювані символи, лігатури та складні гліфи
Деякі символи навмисно дублюються з міркувань сумісності. Таким чином, грецький символ Mu “µ”, U + 03BC і мікросимвол “μ”, U + 03B5 дублюються з історичних причин сумісності з Latin-1.
Всі грецькі літери кодуються у грецькому розділі Unicode, але багато кодуються вдруге під назвою технічного символу, який вони представляють.
Крім того, існують готові форми багатьох загальних лігатур та інших загальновживаних ознак:
- еліпсис "...",
- Римські цифри (Ⅶ…),
- і багато лігатур, таких як "œ", очевидно, або навіть "ff".
Ці еквівалентності ускладнюють порівняння та сортування рядків. Ось чому стандарт визначає правила еквівалентності та сортування.
Юнікодова еквівалентність
Unicode забезпечує два поняття еквівалентності: канонічну та сумісність, перша є підмножиною другої. Наприклад, символ n, за яким йде діакритична тильда
є канонічно еквівалентною і, отже, сумісною з єдиним символом Unicode -, тоді як друкарська лігатура ff сумісна лише з послідовністю двох символів f.
Unicode визначає два критерії еквівалентності - канонічну форму (NF) та сумісність (NFK) - і асоціює їх із попередньо складеними та складеними формами.
NFC Форма нормалізації Канонічний склад нормалізує ланцюг відповідно до його канонічної форми з попередньо складеними знаками NFD Форма нормалізації канонічного розкладання нормалізує ланцюг відповідно до його канонічної форми із складеними знаками NFKC Склад сумісності форми нормалізації нормалізує ланцюг відповідно до його сумісності за формою із попередньо складеними знаками NFKC Склад сумісності форми нормалізації нормалізує ланцюг відповідно до його сумісності за формою із складними ознаками
Невеликий приклад у JavaScript.
Тут використання попередньо складеної чи складеної форми не матиме жодного впливу. З іншого боку, у випадку стандартизації перед зберіганням, наприклад, краще використовувати несполучену форму. Дійсно, будь-який попередньо складений символ може бути змінений у його складеному вигляді, навпаки, це не відповідає дійсності.
З іншого боку, форма сумісності руйнівна. Це може покращити пошук - "f" справді отримає відповідність "ff" після нормалізації NFK - але гліф, безперечно, втрачає значення, як і число в нижньому або верхньому індексі.
Омогліфи
Хоча візуально ідентичні, "А", "А" та "Α" - це три різні символи. Про них кажуть, що це гомогліфи. У цьому прикладі ми маємо відповідно велику латинку "А", велику кирилицю "А" та велику грецьку "Α".
Ми відразу бачимо, які проблеми це може становити ... Реєстрація веб-сайту з унікальним псевдонімом? Якщо “Антуан” існує, то я можу зареєструватися в “Антуан”.
На жаль, Unicode не дуже допомагає нам тут. Існує таблиця різних ознак, які можуть спричинити проблеми. В якості альтернативи існує модуль для Java та JavaScript, який може виявити наявність гомогліфів.
Підсумовуючи
Ми усвідомлюємо, що майже у 100% випадків, якщо ви не пишете мовами, що використовують ідеограми, найбільш підходящим кодуванням для зберігання та обміну інформацією є UTF-8. Це дозволяє кодувати всі палітри символів Unicode, обмежуючи при цьому простір.
Для програм корисно стандартизувати оброблений текст, щоб можна було ефективно сортувати та порівнювати рядки. Крім того, ми також можемо обмежити використання гомогліфів, якщо цього вимагають обмеження безпеки.
Приєднуйтесь до обговорення !
Квентін Бусуттіл - ліцензія Creative Commons BY-NC-ND