Прапорець y дозволяє виконувати пошук на вказаній позиції у вихідному рядку.
Щоб дізнатись як використовувати прапорець y, і краще зрозуміти шляхи використання регулярних виразів, розгляньмо приклад з практики.
Одним із поширених завдань для регулярних виразів є “лексичний аналіз”: для прикладу ми розглядаємо тест написаний певною мовою програмування і хочемо виділити структурні елементи. Наприклад, HTML містить теги та атрибути, код JavaScript – функції, змінні тощо.
Написання лексичних аналізаторів – це особлива сфера, зі своїми інструментами та алгоритмами, тому ми не будемо заглиблюватись в неї, а зосередимось на звичайному завданні: прочитати щось на заданій позиції.
Наприклад, у нас є рядок коду let varName = "значення", і нам потрібно прочитати з нього назву змінної, яка починається з позиції 4.
Ми шукатимемо назву змінної за допомогою регулярного виразу \w+. Насправді імена змінних JavaScript потребують трохи складніших регулярних виразів для точної відповідності, але тут це не важливо.
- Виклик
str.match(/\w+/)знайде лише перше слово в рядку (let). А це не те що нам потрібно. - Ми можемо додати прапорець
g. Але тоді викликstr.match(/\w+/g)шукатиме всі слова в тексті, тоді як нам потрібно лише одне слово на позиції4.
Отже, як змусити регулярний вираз шукати саме на заданій позиції?
Спробуймо використати метод regexp.exec(str).
Для regexp без прапорців g і y, цей метод шукає лише перший збіг, він працює, так само як str.match(regexp).
…Але якщо є прапорець g, тоді він виконує пошук у str, починаючи з позиції, збереженої у властивості regexp.lastIndex. І, якщо він знаходить збіг, то змінює regexp.lastIndex на індекс одразу після збігу.
Іншими словами, regexp.lastIndex служить відправною точкою для пошуку, і кожен виклик regexp.exec(str) встановлює нове значення (“після останнього збігу”). Звичайно, якщо є прапорець g.
Отже, послідовні виклики regexp.exec(str) повертають збіги один за одним.
Ось приклад таких викликів:
let str = 'let varName'; // Знайдімо всі слова в цьому рядку
let regexp = /\w+/g;
alert(regexp.lastIndex); // 0 (спочатку lastIndex=0)
let word1 = regexp.exec(str);
alert(word1[0]); // let (перше слово)
alert(regexp.lastIndex); // 3 (позиція після збігу)
let word2 = regexp.exec(str);
alert(word2[0]); // varName (друге слово)
alert(regexp.lastIndex); // 11 (позиція після збігу)
let word3 = regexp.exec(str);
alert(word3); // null (більше немає збігів)
alert(regexp.lastIndex); // 0 (скидається в кінці пошуку)
Ми можемо отримати всі збіги в циклі:
let str = 'let varName';
let regexp = /\w+/g;
let result;
while (result = regexp.exec(str)) {
alert( `Found ${result[0]} at position ${result.index}` );
// Знайдено let на позиції 0, після
// Знайдено varName на позиції 4
}
Таке використання regexp.exec є альтернативою методу str.matchAll, з трохи більшим контролем над процесом.
Повернемося до нашого завдання.
Ми можемо вручну встановити lastIndex на 4, щоб почати пошук із заданої позиції!
Ось так:
let str = 'let varName = "значення"';
let regexp = /\w+/g; // без прапорця "g", властивість lastIndex ігнорується
regexp.lastIndex = 4;
let word = regexp.exec(str);
alert(word); // varName
Ура! Проблема вирішена!
Ми здійснили пошук \w+, починаючи з позиції regexp.lastIndex = 4.
І результат нашого пошуку правильний.
…Але заждіть, не так швидко.
Зауважте: виклик regexp.exec починає пошук із позиції lastIndex, а потім продовжує пошук. Якщо на позиції lastIndex немає слова, але воно знаходиться десь після неї, тоді воно буде знайдено:
let str = 'let varName = "значення"';
let regexp = /\w+/g;
// почати пошук з позиції 3
regexp.lastIndex = 3;
let word = regexp.exec(str);
// знайдено збіг на позиції 4
alert(word[0]); // varName
alert(word.index); // 4
Для деяких завдань, зокрема лексичного аналізу, це неправильно. Нам потрібно знайти збіг в заданій позиції в тексті, а не десь після неї. Це і є головне призначення прапорця y.
Прапорець y змушує regexp.exec шукати саме на позиції lastIndex, а не “починаючи з” неї.
Ось той самий пошук із прапорцем y:
let str = 'let varName = "значення"';
let regexp = /\w+/y;
regexp.lastIndex = 3;
alert( regexp.exec(str) ); // null (на позиції 3 пробіл, а не слово)
regexp.lastIndex = 4;
alert( regexp.exec(str) ); // varName (слово на позиції 4)
Як ми бачимо, регулярний вираз /\w+/y не знаходить збігів на позиції 3 (на відміну від регулярного виразу з прапорцем g), але знаходить збіг на позиції 4.
Але це не всі переваги використання прапорця y, він також збільшує продуктивність пошуку.
Уявіть, у нас довгий текст, а в ньому зовсім немає збігів. Тоді пошук із прапорцем g буде йти до кінця тексту й нічого не знайде, і це займе значно більше часу, ніж пошук із прапорцем y, який перевіряє лише на вказаній позиції.
Лексичний аналіз часто вимагає пошук на конкретній позиції. Використання прапорця y є ключем до правильної реалізації та хорошої продуктивності при виконанні таких завдань.
Коментарі
<code>, для кількох рядків – обгорніть їх тегом<pre>, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)