Іноді, нам потрібно знайти тільки такі співпадіння з шаблоном, за якими слідує, або яким передує інший шаблон.
Для цього існують спеціальні синтаксичні конструкції, котрі називається “перевірка уперед” та “перевірка назад”.
Для початку, давайте знайдемо ціну у рядку 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 |
Коментарі
<code>, для кількох рядків – обгорніть їх тегом<pre>, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)