Regex: Флаг sticky «y», поиск по позиции
Флаг y (sticky) в регулярных выражениях JavaScript привязывает каждый поиск к точной позиции lastIndex — без сканирования вперёд.
Флаг y — флаг sticky — является одним из флагов, которые можно добавить к регулярному выражению JavaScript. Он привязывает каждый поиск к одной конкретной позиции: lastIndex регулярного выражения. Если совпадение не может начаться точно в lastIndex, поиск завершается неудачей — движок не сканирует строку вперёд в поисках совпадения в другом месте.
Эта страница рассказывает о том, что делает флаг sticky, чем он отличается от глобального флага (g), как читается и обновляется lastIndex, о распространённых ловушках и о том, когда флаг действительно полезен (токенизаторы и последовательные парсеры).
Концепция флага sticky (y)
Флаг sticky (y) гарантирует, что совпадение начнётся точно с индекса, хранящегося в свойстве lastIndex объекта регулярного выражения. В этом и состоит ключевое отличие от глобального флага (g): g позволяет движку сканировать строку вперёд начиная с lastIndex в поисках следующего совпадения в любом более позднем месте, тогда как y требует, чтобы совпадение начиналось именно в lastIndex — иначе поиск завершается неудачей. Поведение «без сканирования вперёд» — это именно то, что нужно токенизатору: оно гарантирует, что входные данные обрабатываются непрерывно, без неожиданных пробелов между токенами.
y не сканирует вперёд — g сканирует
Наагляднее разницу видно, если дать обоим флагам шаблон, который не совпадает в позиции 0:
Флаг g находит abc, несмотря на то что совпадение начинается с индекса 3. Флаг y возвращает null, потому что в позиции 0 находится ., а не начало abc.
В приведённом примере флаг y гарантирует, что второй вызов test начинает поиск с позиции, следующей сразу за предыдущим совпадением. Стоит обратить внимание на два момента:
- Успешное совпадение сдвигает
lastIndexк индексу, следующему сразу за совпадением (0 → 3 → 6). Это автоматическое продвижение позволяет вызыватьtest/execв цикле. - Неудачное совпадение сбрасывает
lastIndexв 0. После того как третий вызов возвращаетfalse,lastIndexснова равен 0 и готов к новому поиску.
lastIndex читается и записывается
lastIndex — это не просто входное значение, которое вы устанавливаете: движок обновляет его при каждом вызове test/exec с флагом y (или g). Поэтому обычная схема работы такова: установите lastIndex один раз, если хотите начать не с позиции 0, а затем предоставьте управление циклу.
Sticky также влияет на якоря
При использовании флага y якорь начала строки ^ не перепривязывается к lastIndex — ^ по-прежнему означает «начало всей строки» (или начало строки при флаге m). Поведение sticky обеспечивается неявным правилом «совпадение в lastIndex», а не переопределением ^. Поэтому шаблон вроде /^x/y будет неудачным при любом lastIndex больше 0, поскольку ^ не может быть выполнено там.
Практическое применение флага sticky
Разбор потоков данных
Флаг sticky особенно полезен при разборе потоков данных, когда требуется последовательно сопоставлять токены.
Токенизация строк
Ещё один распространённый сценарий использования — токенизация строк, при которой необходимо гарантировать, что токенизатор переходит от одного совпадения к следующему без пропуска символов.
Поиск с заданной позиции
Свойство lastIndex совместно с флагом sticky позволяет искать шаблоны начиная с определённой позиции в строке.
В этом примере установка lastIndex в 6 позволяет методу test найти слово "world" начиная с указанной позиции.
Комбинирование флага sticky с другими флагами
Флаг sticky можно комбинировать с другими флагами, например g (global), для расширения возможностей сопоставления с шаблоном. В данном примере флаг g обеспечивает глобальный поиск по всей строке, а флаг y гарантирует, что каждое совпадение начинается точно в позиции lastIndex. Такая комбинация позволяет выполнять глобальный поиск со строгим последовательным поведением флага sticky.
В этом примере мы изменили регулярное выражение так, чтобы оно поглощало разделительную запятую. Это гарантирует, что следующее совпадение начнётся в правильной позиции без ручного управления индексом.
Расширенные примеры
Извлечение пар «ключ — значение»
Рассмотрим более сложный сценарий, в котором нужно извлечь пары «ключ — значение» из строки с различными разделителями. Каждая часть пары извлекается с помощью группы захвата — match[1] содержит ключ, а match[3] — значение.
Разбор логов
Флаг sticky может быть чрезвычайно полезен при разборе структурированных логов, где записи должны сопоставляться с определённых позиций.
Обратите внимание: группа для сообщения использует [^;]+ («всё до следующей ;``»), а не \w+. При \w+сообщение, содержащее пробел, прервало бы совпадение на середине; поскольку флаг sticky отказывается пропускать вперёд, *следующий* вызовexec` завершился бы неудачей и цикл прекратился бы досрочно — молча отбросив оставшиеся записи. Это самая распространённая ловушка флага sticky: ваш шаблон должен непрерывно поглощать входные данные, иначе разбор остановится.
Когда использовать y (и когда не стоит)
Обратитесь к флагу sticky, когда:
- Вы пишете токенизатор или парсер и вам необходимо непрерывно обрабатывать входные данные, один токен за раз, без пробелов.
- Вы хотите проверить, совпадает ли шаблон в одной конкретной позиции, не допуская, чтобы движок искал совпадение дальше по строке.
Предпочтите обычный глобальный флаг (g), если вам просто нужны все совпадения в любом месте строки и вам не важно, являются ли они смежными — например, при поиске всех адресов электронной почты в документе.
Помните о двух правилах, которые являются источником большинства ошибок при работе с флагом sticky: неудачное совпадение сбрасывает lastIndex в 0, а ваш шаблон должен поглощать каждый символ между совпадениями, иначе цикл прервётся досрочно.
Заключение
Флаг sticky (y) меняет «сканирование по всей строке» флага g на строгое, позиционно-зафиксированное сопоставление в lastIndex. Именно такой компромисс нужен при создании токенизаторов и последовательных парсеров, где важна непрерывность обработки. Комбинируйте его с группами захвата для извлечения структурированных данных и следите за lastIndex — как только вы поймёте, как он сдвигается и сбрасывается, работа с флагом sticky станет точным и предсказуемым инструментом.