Como funciona a expressão regular ‘(? <= #) + (? = #)?

Eu tenho o seguinte regex em um programa c # e tenho dificuldades em entendê-lo:

(?<=#)[^#]+(?=#) 

Vou dividir o que acho que entendi:

 (?<=#) 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 `?=`? 

Então o problema que tenho é a parte ?<= e ?< . Na leitura do MSDN, ? é usado para nomear grupos, mas, nesse caso, o colchete angular nunca é fechado.

Não consegui encontrar ?= Nos documentos, e procurá-lo é realmente difícil, porque os motores de busca irão ignorar os caracteres especiais.

Eles são chamados de lookarounds; eles permitem que você defina se um padrão corresponde ou não, sem realmente fazer a correspondência. Existem 4 lookarounds básicos:

  • Lookarounds positivos: veja se podemos combinar com o pattern
    • (?=pattern) – … à direita da posição atual (olhar em frente )
    • (?< =pattern) - ... à esquerda da posição atual (veja atrás )
  • Olhares negativos - ver se não podemos corresponder ao pattern
    • (?!pattern) - ... para a direita
    • (?< !pattern) - ... para a esquerda

Como um lembrete fácil, para um lookaround:

  • = é positivo ! é negativo
  • < olha para trás , senão olha para frente

Referências

  • regular-expressions.info/Lookarounds

Mas por que usar lookarounds?

Pode-se argumentar que lookarounds no padrão acima não são necessários, e #([^#]+)# fará o trabalho muito bem (extraindo a string capturada por \1 para obter o não- # ).

Não é bem assim. A diferença é que, uma vez que um lookaround não corresponde ao # , ele pode ser "usado" novamente pela próxima tentativa de encontrar uma correspondência. Em termos simplistas, lookarounds permitem que "correspondências" se sobreponham.

Considere a seguinte string de input:

 and #one# and #two# and #three#four# 

Agora, #([az]+)# dará as seguintes correspondências ( como visto no rubular.com ):

 and #one# and #two# and #three#four# \___/ \___/ \_____/ 

Compare isso com (?< =#)[az]+(?=#) , Que corresponde a:

 and #one# and #two# and #three#four# \_/ \_/ \___/ \__/ 

Infelizmente isso não pode ser demonstrado no rubular.com, já que não suporta lookbehind. No entanto, ele suporta lookahead, então podemos fazer algo similar com #([az]+)(?=#) , Que corresponde ( como visto no rubular.com ):

 and #one# and #two# and #three#four# \__/ \__/ \____/\___/ 

Referências

  • regular-expressions.info/ Comparação de Flavor

Como outro pôster mencionado, estes são lookarounds , construções especiais para alterar o que é correspondido e quando. Isso diz:

 (?< =#) 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 

Então, isso irá combinar todos os caracteres entre dois # s.

Lookaheads e lookbehinds são muito úteis em muitos casos. Considere, por exemplo, que a regra "corresponde a todos os b não seguidos por um a ". Sua primeira tentativa pode ser algo como b[^a] , mas isso não está certo: isso também irá combinar o bu no bus ou o bo no boy , mas você só queria o b . E isso não combina com o b na cab , mesmo que não seja seguido por um a , porque não há mais caracteres para combinar.

Para fazer isso corretamente, você precisa de uma visão antecipada: b(?!a) . Isto diz "combine a b mas não combine um a depois, e não faça essa parte da partida". Assim, ele vai combinar apenas o b no bolo , que é o que você quer; Da mesma forma, combinará com o b na cab .

Eles são chamados de look-arounds : http://www.regular-expressions.info/lookaround.html