Регулярные выражения очень эффективны, если вы хотите найти или извлечь определенный шаблон в строке. Однако с большой силой приходит большая ответственность. Регулярные выражения могут привести к неожиданным результатам (lazy vs greedy matching) и они могут быстро стать ресурсоемкими (catastrophic backtracking и repeated capture groups).
Регулярные выражения также часто неправильно понимаются. Небольшое регулярное выражение может легко потреблять много вычислительной мощности при неправильном использовании. В качестве примера рассмотрим это регулярное выражение (.*)+b
. Это выражение может не выглядеть опасным, но если вы выполните его для большей строки, которая не содержит b
, это выражение приведет к циклу, который будет длиться более 10 секунд.
По этим причинам мы ограничили регулярные выражения в АппОптима. Однако почти во всех случаях это не ограничит вашу функциональность. Это может заставить вас узнать немного больше о регулярных выражениях.
Ограничения
Ознакомьтесь с приведенными ниже ограничениями, чтобы понять, как использовать регулярные выражения в контексте АппОптима.
Количественные или повторяющиеся группы не допускаются
Количественные группы обычно не нужны, и поскольку они могут легко выйти из-под контроля, мы их не допускаем. Чтобы лучше понять это, перейдите на https://regex101.com и оцените следующее регулярное выражение:
Regex: (a+)+b
Value:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Вы заметите, что это приведет к тайм-ауту. В данном конкретном случае проблема заключается в обратном ходе группы захвата. Мы решили не разрешать использование повторяющихся групп вообще.
Greedy matches в группах против possessive matches
АппОптима не допускает Greedy matches в группах. Вместо этого используйте lazy или possessive matches.
Прототипом регулярного выражения является термин .*
. Greedy match определяется как Совпадения от нуля до неограниченного количества раз, столько раз, сколько возможно, возвращая обратно по мере необходимости (Greedy). Проблема здесь в столько раз и возвращении обратно. Люди обычно используют это для сопоставления чего-либо, пока не будет выполнено граничное условие. Рассмотрим следующее сопоставление регулярного выражения:
Regex: key: (.*);
Value: something. key: value; something else
Механизм регулярных выражений сначала ищет key:
и затем greedy сопоставляет группу (.*)
до конца строки . Затем он проверяет, есть ли ;
после этого совпадения ; чего никогда не будет. Затем ему приходится возвращаться по одному символу за раз. Каждый раз он сначала проверяет, совпадает ли группа каждый раз, пока не найдет ;
. В качестве небольшой детали, ему также придется каждый раз запоминать новое значение для группы; так как это группа захвата. Все это очень ресурсоемко и неэффективно; и, что более важно, сложность возрастает с длиной строки, которую нужно сопоставить.
Лучший способ сделать это следующий:
Regex: key: ([^;]*+)
Value: something. key: value; something else
Это соответствие будет соответствовать до тех пор, пока не будет найдено ;
после key:
. Плюс также превращает это в possessive match, определяемое как: Совпадает от нуля до неограниченного количества раз, столько раз, сколько возможно, без возврата (possessive). Ключевым моментом здесь является то, что он не возвращается. Другими словами, как только что-то совпало, движок не будет пересматривать и не будет смотреть назад, а только вперед. Именно поэтому нам нужно уточнить соответствие от «все» (.
) до «все, но не ;
». Нам нужно сказать ему, чтобы он соответствовал всему, но не ;
.
Если сравнить первое регулярное выражение против второго в отладчике регулярных выражений (перейдите по двум ссылкам) вы также заметите, что первый занимает 42 шага, а второй — 9! Вот почему мы рекомендуем вам использовать possessive matches в целом.
Группы не допускаются для простых матчей.
Группы захвата в регулярных выражениях используются для извлечения информации. Иногда эта концепция неправильно используется для простых совпадений поиска. Поскольку это только добавляет накладные расходы и не является необходимым, АппОптима не допускает этого. В большинстве случаев можно использовать атомарные группы вместо этого. Для обеспечения высокой производительности продукта в определениях регулярных выражений могут применяться дополнительные ограничения группировки, например, в правилах именования запросов .
АппОптима допускает только одну группу захвата в местах, где вам нужно извлечь значение. Логика здесь такова: если регулярное выражение содержит группу, АппОптима будет ее использовать. В противном случае для извлечения используется полное совпадение.
Это означает, что вам вообще не нужно использовать группы захвата. В большинстве сценариев извлечения достаточно просто определить, что вы хотите получить. В случае извлечения следующее регулярное выражение захватит значение — группа захвата не нужна.
Если вы хотите, например, получить версию Windows из строки пользовательского агента, вы можете использовать следующее регулярное выражение:
Regex: Windows NT[^)]+
Value: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0 Safari/537.36
Это позволит захватить данные Windows NT 10.0
, и в группе захвата нет необходимости.
АппОптима не допускает обратных ссылок
Обратные ссылки часто используются для решения иерархических или рекурсивных соответствий, оба из которых с большой вероятностью могут оказаться очень затратными и не нужны в АппОптима.
Другие распространенные заблуждения
Часто люди предполагают, что им нужно сопоставить полный текст ввода. Обычно это приводит к такому регулярному выражению:
Regex: .*key: [^;]*+.*
Value: something. key: value; something else
Это соответствует полной строке, а не только тому, что нужно. Это основано на недоразумении, что заданное регулярное выражение всегда будет соответствовать полному тексту, тогда как обычно оно используется для поиска подстроки. В АппОптима условия регулярного выражения обычно ищут шаблон и не сопоставляют принудительно весь ввод. Начальные и конечные (.*
) соответствия не нужны, поэтому, пожалуйста, не используйте их.