Ваше регулярное выражение может быть разбито следующим образом.
[Пробелы и символы новой строки предназначены для ясности]
\( match a literal (
[\s\S]* match 0 or more of whitespace or not-whitespace (anything)
[^\\]{1} match 1 thing which is not \
\)? optionally match a literal )
regex101 demo
Это тот [\s\S]*
, который в конечном итоге хлюпает во всем.
?
на конце не означает ленивость, это делает сопоставление )
необязательным. Чтобы быть ленивым, ?
нужно поставить перед открытым квалификатором, например *?
или +?
, {3,}?
или {1,5}?
.
Чтобы соответствовать только первому набору круглых скобок, мы хотим лениво сопоставлять что-либо между неэкранированными скобками. Ленивое сопоставление чего-либо - это просто .*?
.
Сопоставление неэкранированных скобок немного сложнее. Мы могли бы сопоставить [^\\]\)
, но для этого требуется соответствующий символ. Это не сработает, если открывающая скобка находится в начале строки, потому что перед (
нет символа. Мы можем решить эту проблему, также сопоставив начало строки: (?:[^\\]|^)\)
.
(?: non-capturing group
[^\\] match a non \
| or
^ the beginning of the string
)
\( match a literal (
.*? lazy match 0 or more of anything
[^\\] match a non \
\) match a literal )
regex101 demo
Но это будет сорвано ()
. Соответствует всем ()(foo)
.
(?:[^\\]|^)
соответствует началу строки. \(
соответствует первому (
. Остается .*?[^\\]\)
смотрит на )(foo)
. Первый )
не соответствует, потому что нет ведущего символа, он уже был использован. Итак, .*?
поглощает символы, пока не получит его o)
, которое соответствует [^\\]\)
.
Граничную проблему лучше решить отрицательным взглядом назад . (?<!\\)
говорит, что предыдущий символ не должен быть \
, который вообще не включает никаких символов. Взгляд назад не потребляет то, что совпадает, поэтому его можно использовать, чтобы заглянуть вперед и назад. Большинство, но не все движки регулярных выражений поддерживают их.
(?<!\\) \( match a literal ( which is not after a \
.*? lazy match 0 or more of anything
(?<!\\) \) match a literal ) which is not after a \
regex101 demo
Однако есть библиотеки для синтаксического анализа User-Agents. ua-parser имеет библиотеки для многих языков,