Ответ 1
TL; DR
Почему первый шаблон настолько менее эффективен, чем второй, и, наиболее смутно, , почему третий настолько эффективен?
Поскольку первые два привязаны, третье - нет.
Реальная история, как предпринимаются шаги
Учитывая это регулярное выражение /^x/gm
, сколько шагов, по вашему мнению, потребуется движку для возврата "нет совпадения", если строка темы abc
? Вы правы, два.
- Утверждение начала строки
- Соответствие
x
Тогда полное совпадение завершится неудачно, так как без x
сразу после начала утверждения строки.
Хорошо, я соврал. Это не то, что я противный, это просто облегчает понимание вещей, которые произойдут. Согласно regex101.com он не предпринимает никаких шагов:
На этот раз вы поверите? Да. Нет. Посмотрим.
PCRE
оптимизация запуска
PCRE
, будучи добрым для своих пользователей, предоставляет некоторые функции для ускорения тем, что называется оптимизацией при запуске. Он выполняет некоторые зависимые оптимизации в соответствии с используемыми регулярными выражениями.
Одной из важных особенностей этих оптимизаций является предварительная проверка темы, чтобы гарантировать, что:
- строка subject содержит по крайней мере один символ, который соответствует первому символу соответствия или
- если существует известная начальная точка.
Если не найдено, соответствующая функция никогда не запускается.
Говоря о том, что если наше регулярное выражение /x/
, а наша строка темы abc
, то при включенной оптимизации запуска предварительная проверка предназначена для поиска x
, если не найдена целая матч терпит неудачу или лучше, он даже не хочет проходить процесс сопоставления.
Итак, как эта информация помогает?
Вернемся к нашему первому примеру и немного изменим наше регулярное выражение. From:
/^x/gm
к
/^x/g
Разница - это флаг m
, который становится неустановленным. Для тех, кто не знает, что делает флаг m
if, если:
Он изменяет значение символов ^
и $
в том смысле, что они больше не означают начало и конец строки, но начало и конец строки.
Теперь, если мы запустим это регулярное выражение /^x/g
по нашей теме abc
? Должны ли мы ожидать разницу в шагах, которые принимает двигатель или нет? Абсолютно да. Посмотрим regex101.com вернула информацию:
Я действительно призываю вас поверить в этот раз. Это актуально.
Что происходит?
Ну, это кажется немного запутанным, но мы собираемся просветить все. Когда не существует набора модификаторов m
, предварительное сканирование проверяет начало строки (известная начальная точка). Если утверждение прошло, тогда выполняется фактическая функция сопоставления, иначе возвращается "no match".
Но подождите... каждая строка предмета определенно имеет одно и только начало строковой позиции и всегда в самом начале. Значит, не было бы предварительного сканирования явно ненужным? Да, двигатель не выполняет предварительную проверку. С помощью /^x/g
он сразу же утверждает начало строки, а затем терпит неудачу (так как она совпадает с ^
, она проходит через фактический процесс сопоставления). Вот почему мы видим regex101.com показывает количество шагов 2.
Но... с настройкой модификатора m
разные. Теперь значение обоих якорей ^
и $
изменяется. С ^
совпадением начала строки утверждение одного и того же положения в строке субъекта abc
происходит, но следующий непосредственный символ не x
, находящийся в рамках фактического процесса сопоставления, и поскольку флаг g
включен, следующее совпадение начинается в позиции до b
и не выполняется, и эта пробная версия продолжается до конца строки темы.
Отладчик показывает шаги 6, но на главной странице указаны шаги 0, почему?
Я не уверен в последнем, но ради отладки, отладчик regex101 работает с (*NO_START_OPT)
, поэтому 6 шагов являются истинными, только если этот глагол установлен. И я сказал, что не уверен в последнем, потому что все привязанные шаблоны предотвращают дальнейшую предварительную оптимизацию, и мы должны знать, что можно назвать привязанный шаблон:
Рисунок автоматически привязывается к PCRE, если все его альтернативы верхнего уровня начинаются с одного из следующих:
^
еслиPCRE_MULTILINE
не установлен\A
всегда\G
всегда.*
, еслиPCRE_DOTALL
установлено, и нет обратных ссылок на subpattern, в котором.*
появляется
Теперь вы поняли, о чем я говорил, когда я говорил, что предварительное сканирование не происходит, когда флаг m
не установлен в /^x/g
: он считал привязанный шаблон, оптимизация сканирования. Поэтому, когда флаг m
включен, это уже не привязанный шаблон: /^x/gm
, поэтому может произойти оптимизация предварительного сканирования.
Двигатель знает, что начало привязки строки \A
(или ^
в то время как многострочный режим отключается) происходит только один раз, когда он согласован, чтобы он не продолжался в следующей позиции.
Вернуться к собственным RegExes
Первые два привязаны (^
в сочетании с флагом m
), третий - нет. То есть, третье регулярное выражение выигрывает от оптимизации предварительного сканирования. Вы можете поверить в шаги 35, так как оптимизация вызвала это. Но если вы отключите оптимизацию запуска:
(*NO_START_OPT)!next(?:(?<=^.{5})|(?=$))
Вы увидите 57 шагов, которые в основном совпадают с количеством шагов отладчика.