Декільки символів, або класи символів всередині квадратних дужок […]
означають “шукати будь-який символ з-поміж заданих”.
Набори
До прикладу, [eao]
означає будь-який з трьох символів: 'a'
, 'e'
, or 'o'
.
У такий спосіб записується так званий набір. Набір може використовуватись в регулярних виразах разом зі звичайними символами:
// знайти [t або m], а потім "op"
alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top"
Зверніть увагу, що незважаючи на те, що в наборі вказано два символи, в результаті є співпадіння лише по одному з них.
Тож нижченаведений приклад не знайде співпадінь:
// знайти "V", потім [o чи i], потім "la"
alert( "Voila".match(/V[oi]la/) ); // null, no matches
Шаблон шукає:
V
,- потім одна з літер набору
[oi]
, - потім
la
.
Результатом такого пошуку могли б стати варіанти Vola
або Vila
.
Діапазони
Квадратні дужки також можуть містити діапазони символів.
До прикладу, [a-z]
шукатиме символу в діапазоні від a
до z
, а [0-5]
дорівнює цифрам в діапазоні від 0
до 5
.
В нижченаведеному прикладі ми шукатимемо літеру "x"
за якою слідують дві цифри, або літери від A
до F
:
alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF
Тут [0-9A-F]
має в собі одразу два діапазони: він шукає символ, який є або цифрою від 0
до 9
, або літерою від A
до F
.
Якби ми захотіли шукати літери не тільки верхнього, а й нижнього регістру, ми могли б додати діапазон a-f
: [0-9A-Fa-f]
. Або додати флаг i
.
Крім того, всередині […]
ми можемо використовувати символьні класи.
До прикладу, якщо ми захочемо знайти символ “слова” \w
, або дефіс -
, набір виглядатиме наступним чином [\w-]
.
Комбінувати декілька класів теж можливо, наприклад [\s\d]
означає “пробіл, або цифра”.
До прикладу:
- \d – це те саме, що й
[0-9]
, - \w – це те саме, що й
[a-zA-Z0-9_]
, - \s – це те саме, що й
[\t\n\v\f\r ]
, плюс декілька інших рідкісних пробільних символів Unicode.
Приклад: \w в інших мовах світу
Оскільки символьний клас \w
це лише скорочений запис [a-zA-Z0-9_]
, він не зможе знайти китайські ієрогліфи, літери кирилицею тощо.
Існує спосіб написати більш універсальний шаблон, що включатиме в себе буквенні символи будь-якої мови світу. Це легко реалізувати завдяки властивостям Unicode: [\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]
.
Давайте розшифруємо цей шаблон. Подібно до \w
, ми створюємо свій діапазон, який включає в себе символи з наступними властивостями Unicode:
Alphabetic
(Alpha
) – для літер,Mark
(M
) – для акцентів,Decimal_Number
(Nd
) – для цифр,Connector_Punctuation
(Pc
) – для нижнього підкреслення'_'
і тому подібних символів,Join_Control
(Join_C
) – два спеціальних коди200c
та200d
, які використовуються у лігатурах, зокрема в арабській мові.
Шаблон в дії:
let regexp = /[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]/gu;
let str = `Hi 你好 12`;
// знайти всі літери та цифри:
alert( str.match(regexp) ); // H,i,你,好,1,2
Звичайно, ми можемо редагувати вищенаведений шаблон: додавати Unicode властивості, або видаляти їх. Дізнатись більше про Unicode властивості можна за посиланням Юнікод: прапорець "u" та клас \p{...}.
Unicode властивості p{…}
недоступні у Internet Explorer. Втім, якщо вони нам все ж потрібні, ми можемо скористатись бібліотекою XRegExp.
або просто вказати діапазон потрібних нам символів певною мовою, наприклад [а-я]
для літер кирилицею.
Діапазони виключень
Окрім звичайних діапазонів, існують діапазони “виключень” які виглядають наступним чином: [^…]
.
Вони відрізняються символом каретки ^
на початку і знаходять будь-який символ окрім вказаних в діапазоні.
До прикладу:
[^aeyo]
– будь-який символ окрім'a'
,'e'
,'y'
or'o'
.[^0-9]
– будь-який символ окрім цифр, так само як і\D
.[^\s]
– будь-який не пробільний символ\S
.
Нижченаведений приклад шукає будь-який символ окрім літер латиницею, цифр та пробільних символів:
alert( "alice15@gmail.com".match(/[^\d\sA-Z]/gi) ); // @ та .
Екранування всередині […]
Зазвичай, коли ми хочемо знайти один зі спецсимволів, нам потрібно екранувати його наступним чином \.
. Тобто, якщо нам потрібна зворотня коса риска, ми маємо писати \\
, і так далі.
В квадратних дужках ми можемо використовувати велику кількість спецсимволів без екранування:
- Символ
. + ( )
не потребує екранування. - Дефіс
-
не потребує екранування на початку, або в кінці (тобто коли не може означати діапазон). - Каретка
^
екранується лише на початку (без екранування означає набір символів-виключень). - Закриваюча квадратна дужка
]
завжди потребує екранування (у випадках, коли нам потрібно знайти цей символ).
Інакше кажучи, всі спецсимволи можна використовувати без екранування тоді, коли вони не мають додаткового значення в квадратних дужках.
Крапка .
всередині квадратних дужок означає просто крапку. Шаблон [.,]
шукатиме один з двох символів, або крапку, або кому.
В нижченаведеному прикладі регулярний вираз [-().^+]
шукає один з вказаних символів -().^+
:
// Не потрібно екранувати
let regexp = /[-().^+]/g;
alert( "1 + 2 - 3".match(regexp) ); // Знаходить +, -
…Але якщо ви вирішите все ж таки екранувати їх, “про всяк випадок”, гірше від того не буде:
// Всі символи екрановані
let regexp = /[\-\(\)\.\^\+]/g;
alert( "1 + 2 - 3".match(regexp) ); // так само находить: +, -
Діапазони і прапорець “u”
Якщо в діапазоні є сурогатні пари, для коректної роботи регулярного виразу, прапорець u
є обов’язковим.
До прикладу, давайте знайдемо [𝒳𝒴]
у рядку 𝒳
:
alert( '𝒳'.match(/[𝒳𝒴]/) ); // виводить дивний символ, схожий на [?]
// (пошук було виконано некореткно, повернуто тільки половину символу)
Результат є некоректним, оскільки типово регулярні вирази “не знають” про сурогатні пари.
Регулярний вираз сприймає [𝒳𝒴]
– не як два, а як чотири символи:
- ліва половина
𝒳
(1)
, - права половина
𝒳
(2)
, - ліва половина
𝒴
(3)
, - права половина
𝒴
(4)
.
Якщо вивести їх кодове значення ми побачимо наступне:
for(let i=0; i<'𝒳𝒴'.length; i++) {
alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500
};
Отже, вищенаведений приклад знайшов і вивів ліву половину 𝒳
.
Якщо ми додамо прапорець u
, то поведінка буде коректною:
alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳
Подібна ситуація складається коли ми шукаємо в діапазоні, наприклад [𝒳-𝒴]
.
Якщо ми забудемо додати прапорець u
, то отримаємо помилку:
'𝒳'.match(/[𝒳-𝒴]/); // Error: Invalid regular expression
Причина в тому, що без прапорцю u
сурогатні пари сприймаються як два окремих символи, тобто [𝒳-𝒴]
обробляються як [<55349><56499>-<55349><56500>]
(кожна сурогатна пара замінюється на набір кодів). Таким чином, ми бачимо, що діапазон 56499-55349
є некоректним: його початковий номер 56499
більший за останній 55349
. Це і є причиною помилки.
З прапорцем u
шаблон працює коректно:
// шукає символи від 𝒳 до 𝒵
alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴