У 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{…}
.
За допомогою властивостей Юнікоду ми можемо шукати слова певними мовами, спеціальні символи (лапки, валюти) тощо.