Как работает регулярное выражение '(?<=#)[^#]+(?=#)'?
У меня есть следующее регулярное выражение в программе на C#, и мне трудно понять это:
(?<=#)[^#]+(?=#)
Я разобью это до того, что, я думаю, я понял:
(?<=#) a group, matching a hash. what's `?<=`?
[^#]+ one or more non-hashes (used to achieve non-greediness)
(?=#) another group, matching a hash. what's the `?=`?
Так что проблема у меня заключается в ?<=
а также ?<
часть. Из чтения MSDN, ?<name>
используется для именования групп, но в этом случае угловая скобка никогда не закрывается.
Я не смог найти ?=
в документах, и поиск его действительно сложен, потому что поисковые системы будут в основном игнорировать эти специальные символы.
3 ответа
Они называются двойниками; они позволяют вам утверждать, соответствует шаблон или нет, без фактического соответствия. Есть 4 основных подхода:
- Положительные отзывы: посмотрим, сможем ли мы соответствовать
pattern
...(?=pattern)
- ... справа от текущей позиции (смотреть в будущее)(?<=pattern)
- ... слева от текущей позиции (смотреть сзади)
- Отрицательные взгляды - посмотрим, сможем ли мы НЕ соответствовать
pattern
(?!pattern)
- ... направо(?<!pattern)
- ... налево
В качестве легкого напоминания, для обхода:
=
положительно,!
отрицательно<
это смотреть сзади, иначе это смотреть вперед
Рекомендации
Но зачем использовать обходные пути?
Можно утверждать, что обходные пути в приведенной выше схеме не нужны, и #([^#]+)#
будет делать работу очень хорошо (извлечение строки, захваченной \1
чтобы получить не#
).
Не совсем. Разница в том, что, поскольку внешний вид не соответствует #
, он может быть снова "использован" при следующей попытке найти совпадение. Проще говоря, обходные пути позволяют перекрывать "совпадения".
Рассмотрим следующую входную строку:
and #one# and #two# and #three#four#
Сейчас, #([a-z]+)#
даст следующие совпадения ( как видно на rubular.com):
and #one# and #two# and #three#four#
\___/ \___/ \_____/
Сравните это с (?<=#)[a-z]+(?=#)
, который соответствует:
and #one# and #two# and #three#four#
\_/ \_/ \___/ \__/
К сожалению, это не может быть продемонстрировано на rubular.com, так как он не поддерживает просмотр назад. Тем не менее, он поддерживает lookahead, поэтому мы можем сделать что-то подобное с #([a-z]+)(?=#)
, что соответствует ( как видно на rubular.com):
and #one# and #two# and #three#four#
\__/ \__/ \____/\___/
Рекомендации
Как упоминалось в другом плакате, это обходные пути, специальные конструкции для изменения того, что соответствует и когда. Это говорит:
(?<=#) match but don't capture, the string `#`
when followed by the next expression
[^#]+ one or more characters that are not `#`, and
(?=#) match but don't capture, the string `#`
when preceded by the last expression
Так что это будет соответствовать всем персонажам между двумя #
s.
Lookaheads и lookbehinds очень полезны во многих случаях. Рассмотрим, например, правило "соответствовать всем". b
не сопровождается a
"Ваша первая попытка может быть чем-то вроде b[^a]
, но это не правильно: это также будет соответствовать bu
в bus
или bo
в boy
, но вы хотели только b
, И это не будет соответствовать b
в cab
, хотя это не сопровождается a
, потому что больше нет символов для сопоставления.
Чтобы сделать это правильно, вам нужно посмотреть в будущее: b(?!a)
, Это говорит "соответствуй b
но не соответствует a
впоследствии, и не делайте эту часть матча ". Таким образом, он будет соответствовать только b
в bolo
чего ты хочешь; аналогично это будет соответствовать b
в cab
,
Они называются осмотрами: http://www.regular-expressions.info/lookaround.html