Іноді, нам потрібно знайти тільки такі співпадіння з шаблоном, за якими слідує, або яким передує інший шаблон.
Для цього існують спеціальні синтаксичні конструкції, котрі називається “перевірка уперед” та “перевірка назад”.
Для початку, давайте знайдемо ціну у рядку 1 індичка коштує 30€
. Маємо: число, за яким йде символ €
.
Перевірка уперед
Синтаксис виглядає наступнм чином: X(?=Y)
, це означає "шукай X
, але вважай його співпадінням, тільки якщо за ним слідує Y
". Замість X
та Y
можуть бути будь-які інші шаблони.
Для цілого числа, за яким слідує €
, регулярний вираз виглядатиме наступним чином \d+(?=€)
:
let str = "1 індичка коштує 30€";
alert( str.match(/\d+(?=€)/) ); // 30, число 1 ігнорується, оскільки після нього не стоїть символ €
Зверніть увагу: перевірка уперед це свого роду тест, вміст в дужках (?=...)
не входить до відображуваного регулярним виразом співпадіння 30
.
Коли ми шукаємо X(?=Y)
, регулярний вираз знаходить X
і далі перевіряє наявність Y
одразу після нього. Якщо це не так, тоді потенційне співпадіння пропускається і регулярний вираз продовжує пошук.
Можливі і більш складні тести, наприклад X(?=Y)(?=Z)
означає:
- Знайди
X
. - Перевір, чи
Y
йде одразу післяX
(пропускай, якщо це не так). - Перевір, чи
Z
також йде одразу післяX
(пропускай, якщо це не так). - Якщо обидва тести пройдено, тоді
X
відповідає умовам пошуку, в інщому випадку – продовжуй пошук.
Інакше кажучи, такий шаблон означає, що ми шукаємо на X
за яким одночасно слідують Y
та Z
.
Це можливо тільки за умови, якщо шаблон Y
та Z
не виключають один одного.
Наприклад, \d+(?=\s)(?=.*30)
шукає на \d+
за яким йде пробільний символ (?=\s)
, а також 30
десь після нього (?=.*30)
:
let str = "1 індичка коштує 30€";
alert( str.match(/\d+(?=\s)(?=.*30)/) ); // 1
В нашому рядку цим параметрам повністю відповідає число 1
.
Негативна перевірка уперед
Скажімо, ми хочем знайти кількість, а не ціну в тому самому рядку. Тобто, шукаємо число \d+
, за якийм НЕ слідує €
.
В такому випадку, доречним буде використання негативної перевірки уперед.
Синтаксис виглядає наступним чином: X(?!Y)
, і означає "шукай X
, але за умови, що після нього не йде Y
".
let str = "2 індички коштують 60€";
alert( str.match(/\d+\b(?!€)/g) ); // 2 (ціна не відповідає вимогам шаблону і не відображається в результаті)
Перевірка назад
Зверніть увагу: Перевірка назад не підтримується в браузерах з відміннимим від V8 двигунами, зокрема Safari, Internet Explorer.
Перевірка уперед дозволяє додати умову на кшталт “те, що слідує після”.
Перевірка назад подібна, але дивиться у зворотньому напрямку. Таким чином, вона видає результат, тільки якщо співпадає і шаблон і те, що йде до нього.
Синтаксис наступний:
- Позитивна перевірка назад:
(?<=Y)X
, співпадає зX
, тільки за умови, якщо перед ним єY
. - Негативна перевірка назад:
(?<!Y)X
, співпадаєX
, тільки за умови, якщо перед ним немаєY
.
Наприклад, змінимо ціну з євро на американські долари. Знак долару зазвичай стоїть перед числом, тому, для пошуку $30
ми використовуватимемо (?<=\$)\d+
– сума, перед якою є символ $
:
let str = "1 індичка коштує $30";
// знак долара екрановано \$
alert( str.match(/(?<=\$)\d+/) ); // 30 (число 1 пропущено через відсутність знаку долару перед ним)
Також, якщо нам потрібна кількість – число, якому не передує $
, в такому випадку ми можемо використати негативну перевірку назад (?<!\$)\d+
:
let str = "2 індички коштують $60";
alert( str.match(/(?<!\$)\b\d+/g) ); // 2 (ціна не співпадає з умовами пошуку)
Дужкові групи
Зазвичай, в регулярних виразах вміст в дужках не є частиною співпадіння.
Наприклад, у шаблоні \d+(?=€)
, символ €
не відображається при виведенні співпадінь. Це нормально: ми шукаємо на число \d+
, тоді як (?=€)
це лише перевірка на те, чи дійсно за ним йде символ €
.
Але в деяких ситуаціях ми можемо також потребувати виведення вмісту цього шаблону або його частини. Це можливо. Просто огорніть потрібну частину в додаткові круглі дужки.
В нижченаведеному прикладі знак валюти (€|kr)
теж відображено у результаті, разом із сумою:
let str = "1 індичка коштує 30€";
let regexp = /\d+(?=(€|kr))/; // додаткові круглі дужки навколо €|kr
alert( str.match(regexp) ); // 30, €
І так само для перегляду назад:
let str = "1 індичка коштує $30";
let regexp = /(?<=(\$|£))\d+/;
alert( str.match(regexp) ); // 30, $
Підсумки
Перевірка уперед та назад корисні, коли нам потрібно знайти щось, залежно від контексту до чи після потрібного шаблону.
Для простих регулярних виразів ми можемо виконати подібну задачу вручну. Тобто: відшукати всі співпадіння, у будь-якому контексті, а потім відфільтрувати їх за контекстом за допомогою циклу.
Пам’ятайте, str.match
(без прапорцю g
) і str.matchAll
(завжди) повертає співпадіння у вигляді масиву з властивістю index
, тож ми точно знаємо де саме в тексті вони знаходяться і можемо перевірити контекст.
Але загалом перевірка уперед і назад більш підходящі.
Типи переглядів:
Шаблон | Тип | Співпадіння |
---|---|---|
X(?=Y) |
Позитивна перевірка уперед | X якщо за ним йде Y |
X(?!Y) |
Негативна перевірка уперед | X якщо за ним не йде Y |
(?<=Y)X |
Позитивна перевірка назад | X якщо він йде після Y |
(?<!Y)X |
Негативна перевірка назад | X якщо тільки він не йде після Y |