У JavaScript для рядків використовується кодування Юнікод. Більшість символів кодуються 2-ма байтами, що дозволяє представити максимум 65536 символів.
Цей діапазон недостатньо великий для кодування всіх можливих символів, тому деякі рідкісні символи кодуються 4-ма байтами, наприклад 𝒳 (математичний X) або 😄 (смайл), деякі ієрогліфи тощо.
Ось Юнікод значення для деяких символів:
| Символ | Юнікод | Кількість байтів у Юнікоді |
|---|---|---|
| a | 0x0061 |
2 |
| ≈ | 0x2248 |
2 |
| 𝒳 | 0x1d4b3 |
4 |
| 𝒴 | 0x1d4b4 |
4 |
| 😄 | 0x1f604 |
4 |
Таким чином, такі символи, як a і ≈, займають 2 байти, а коди для 𝒳, 𝒴 і 😄 довші, в них – 4 байти.
Коли створювалась мова JavaScript, кодування Юнікод було простіше: 4-байтових символів не існувало. Тому досі деякі функції мови все ще обробляють їх неправильно.
Наприклад, властивість length вважає, що тут два символи:
alert('😄'.length); // 2
alert('𝒳'.length); // 2
…Але ми бачимо, що лише один, правда ж? Річ у тому, що властивість length трактує 4 байти, як два символи по 2 байти. Це неправильно, адже їх необхідно розглядати тільки разом (так звана “сурогатна пара”, детальніше у розділі Рядки).
Типово регулярні вирази також розглядають 4-байтові “довгі символи” як пару 2-байтових. Як і у випадку з рядками, це може призвести до дивних результатів. Ми побачимо це трохи пізніше, у розділі Набори та діапазони [...].
На відміну від рядків, регулярні вирази мають прапорець u, який виправляє такі проблеми. З таким прапорцем регулярний вираз правильно обробляє 4-байтові символи. Ба більше, стає доступним пошук з використанням властивостей Юнікоду, який ми розглянемо далі.
Властивості Юнікоду \p{…}
Кожен символ в кодуванні Юнікод має багато властивостей. Вони описують, до якої “категорії” належить символ та містять різну інформацію про нього.
Наприклад, якщо символ містить властивість Letter, це означає, що він належить до алфавіту (будь-якої мови). А властивість Number означає, що це цифра: може бути арабська, китайська тощо.
Ми можемо шукати символи за властивістю, записаною як \p{…}. Щоб використовувати \p{…}, регулярний вираз повинен мати прапорець u.
Наприклад, \p{Letter} позначає літеру будь-якою мовою. Ми також можемо використовувати коротший запис \p{L}, оскільки L є псевдонімом Letter. Майже для кожної властивості існують варіанти коротшого запису.
У наведеному нижче прикладі ми будемо шукати три види літер: англійську, грузинську та корейську.
let str = "A ბ ㄱ";
alert( str.match(/\p{L}/gu) ); // A,ბ,ㄱ
alert( str.match(/\p{L}/g) ); // null (немає збігів, \p не працює без прапорця "u")
Ось основні категорії властивостей та їх підкатегорії:
- Літери
L:- мала літера
Ll - модифікатор
Lm, - регістр
Lt, - велика літера
Lu, - інші
Lo.
- мала літера
- Числа
N:- десяткові цифри
Nd, - числа позначені за допомогою літер
Nl, - інші
No.
- десяткові цифри
- Знаки пунктуації
P:- з’єднувачі
Pc, - тире
Pd, - відкриваючі лапки
Pi, - закриваючі лапки
Pf, - відкриваючі дужки
Ps, - закриваючі дужки
Pe, - інші
Po.
- з’єднувачі
- Знак
M(акценти):- двокрапка
Mc, - вкладення
Me, - апострофи
Mn.
- двокрапка
- Символи
S:- валюти
Sc, - модифікатори
Sk, - математичні
Sm, - інші
So.
- валюти
- Розділювачі
Z:- лінія
Zl, - параграф
Zp, - пробіл
Zs.
- лінія
- Інші
C:- контроль
Cc, - форматування
Cf, – не призначенніCn, - для приватного користування
Co, - сурогат
Cs.
- контроль
Наприклад, якщо нам потрібно знайти маленькі літери, ми можемо написати \p{Ll}, знаки пунктуації \p{P} тощо.
Існують також інші похідні категорії, наприклад:
Alphabetic(Alpha), містить в собі літериL, а також числа позначені за допомогою літерNl(наприклад, Ⅻ – символ для римської цифри 12), і деякі інші символиOther_Alphabetic(OAlpha).Hex_Digitмістить шістнадцяткові числа:0-9,a-f.- …тощо.
Юнікод підтримує велику кількість властивостей, і їхній повний перелік зайняв би дуже багато місця, тому ось посилання:
- Перелік усіх властивостей за символом: https://unicode.org/cldr/utility/character.jsp.
- Перелік усіх символів за властивістю: https://unicode.org/cldr/utility/list-unicodeset.jsp.
- Псевдоніми властивостей: https://www.unicode.org/Public/UCD/latest/ucd/PropertyValueAliases.txt.
- Повна база символів Юнікод у текстовому форматі з усіма властивостями: https://www.unicode.org/Public/UCD/latest/ucd/.
Приклад: шістнадцяткові числа
Наприклад, знайдемо шістнадцяткові числа, записані в форматі xFF, де замість F може бути будь-яка шістнадцяткова цифра (0…9 or A…F).
Шістнадцяткову цифру можна позначити як \p{Hex_Digit}:
let regexp = /x\p{Hex_Digit}\p{Hex_Digit}/u;
alert("число: xAF".match(regexp)); // xAF
Приклад: китайські ієрогліфи
Знайдемо китайські ієрогліфи.
Нам допоможе властивість Юнікоду – Script (система письма), яка може мати значення: Cyrillic(Кирилиця), Greek (Грецька), Arabic (Арабська), Han (Китайська) та інші, тут повний перелік.
Для пошуку символів у певній системі письма ми повинні використовувати Script=<value>, наприклад для літер кирилиці: \p{sc=Cyrillic}, для китайських ієрогліфів: \p{sc=Han} тощо.
let regexp = /\p{sc=Han}/gu; // поверне китайські ієрогліфи
let str = `Hello Привіт 你好 123_456`;
alert( str.match(regexp) ); // 你,好
Приклад: валюти
Символи, які позначають валюту, такі як $, €, ¥, мають властивість \p{Currency_Symbol}, короткий псевдонім: \p{Sc}.
Використаємо його для пошуку цін у форматі “валюта, за якою йде цифра”:
let regexp = /\p{Sc}\d/gu;
let str = `Ціни: $2, €1, ¥9`;
alert( str.match(regexp) ); // $2,€1,¥9
Пізніше у розділі Квантифікатори +, *, ? та {n} ми побачимо, як шукати числа, які містять багато цифр.
Підсумки
Прапорець u вмикає підтримку Юнікоду у регулярних виразах.
Це означає дві речі:
- Символи розміром 4 байти обробляються правильно: як один символ, а не як два 2-байтові символи.
- Працює пошук за допомогою властивостей Юнікоду:
\p{…}.
За допомогою властивостей Юнікоду ми можемо шукати слова певними мовами, спеціальні символи (лапки, валюти) тощо.
Коментарі
<code>, для кількох рядків – обгорніть їх тегом<pre>, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)