qual é a diferença entre ?:, ?! e? = em regex?

107

Procurei o significado dessas expressões, mas não consegui entender a diferença exata entre elas. Isso é o que eles dizem:

  • ?: Corresponda a expressão, mas não a capture.
  • ?= Corresponde a um sufixo, mas exclui-o da captura.
  • ?! Corresponde se o sufixo estiver ausente.

Tentei usar isso em RegEx simples e obtive resultados semelhantes para todos. exemplo: as 3 expressões a seguir fornecem resultados muito semelhantes.

  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?!\.[a-zA-Z0-9]+)*
  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?=\.[a-zA-Z0-9]+)*
  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9]+)*
RK Poddar
fonte
Por favor, mostre-nos seu caso de teste. Eles não devem dar os mesmos resultados.
Bergi
@ sepp2k, tem resultados semelhantes em poucos casos, um deles mencionado na pergunta.
RK Poddar
@Bergi, testei com dados aleatórios, contendo palavras em inglês, números de telefone, urls, endereços de e-mail, números, etc.
RK Poddar
4
@RKAgarwal Ah, estou vendo o que você fez aí. Você adicionou um *após os grupos, então eles são simplesmente ignorados.
sepp2k
nota noobie : você só os usaria no início do parêntese, e os parênteses formariam um grupo de captura (diferentes conjuntos de parênteses extraem diferentes seções de texto).
Ryan Taylor

Respostas:

152

A diferença entre ?=e ?!é que o primeiro exige que a expressão fornecida corresponda e o último exige que ela não corresponda. Por exemplo a(?=b), corresponderá ao "a" em "ab", mas não ao "a" em "ac". Visto a(?!b)que corresponderá ao "a" em "ac", mas não ao "a" em "ab".

A diferença entre ?:e ?=é que ?=exclui a expressão de toda a correspondência, ?:mas não cria um grupo de captura. Assim, por exemplo, a(?:b)irá corresponder ao "ab" em "abc", enquanto a(?=b)irá corresponder apenas ao "a" em "abc". a(b)corresponderia ao "ab" em "abc" e criaria uma captura contendo o "b".

sepp2k
fonte
80
?:  is for non capturing group
?=  is for positive look ahead
?!  is for negative look ahead
?<= is for positive look behind
?<! is for negative look behind

Por favor, verifique aqui: http://www.regular-expressions.info/lookaround.html para um tutorial muito bom e exemplos de lookahead em expressões regulares.

anubhava
fonte
15
No entanto, o JavaScript não sabe olhar para trás.
Bergi
1
Este é mais completo para regex geral.
Yan Yang
/ (? <= ^ a) b / funcionou para mim em javascript! Parece não haver nenhum tutorial para olhar para trás em Javascript na internet.
Y. Yoshii
Apenas versões recentes de navegadores começaram a oferecer suporte a look behind em JS
anubhava
- anubhava Eu não conheço nenhuma alternativa para / (? <= ^ A) b / usando a expressão regular pura. Talvez eu possa, mas teria que contar com funções de retorno de chamada.
Y. Yoshii
21

Para entender melhor, vamos aplicar as três expressões mais um grupo de captura e analisar cada comportamento.

  • () grupo de captura - o regex entre parênteses deve ser correspondido e a correspondência criar um grupo de captura
  • (?:) grupo de não captura - o regex entre parênteses deve ser correspondido, mas não cria o grupo de captura
  • (?=) perspectiva positiva - afirma que a regex deve ser correspondida
  • (?!) olhar adiante negativo - afirma que é impossível corresponder ao regex

Vamos pedir q(u)ipara sair . qcorresponde a qe o grupo de captura ucorresponde a u . A correspondência dentro do grupo de captura é realizada e um grupo de captura é criado. Então o motor continua com i. E ivai corresponder a i . Esta última tentativa de correspondência foi bem-sucedida. qui é correspondido e um grupo de captura com u é criado.

Vamos pedir q(?:u)ipara sair . Novamente, qcorresponde a qe o grupo de não captura ucorresponde a u . A correspondência do grupo de não captura é obtida, mas o grupo de captura não é criado. Então o motor continua com i. E ivai corresponder a i . Esta última tentativa de correspondência foi bem-sucedida. qui é compatível

Vamos pedir q(?=u)ipara sair . O lookahead é positivo e é seguido por outro token. Mais uma vez, qcorresponde q e upartidas u . Novamente, a correspondência do lookahead deve ser descartada, de modo que o mecanismo recue ida seqüência para u . A antecipação foi bem-sucedida, então o motor continua com i. Mas inão pode corresponder a você . Portanto, essa tentativa de correspondência falha.

Vamos pedir q(?=u)upara sair . O lookahead é positivo e é seguido por outro token. Mais uma vez, qcorresponde q e upartidas u . A correspondência do lookahead deve ser descartada, de forma que o motor recue uda string para u . A antecipação foi bem-sucedida, então o motor continua com u. E uvai combinar com você . Portanto, esta tentativa de correspondência foi bem-sucedida. qu é compatível

Vamos pedir q(?!i)upara sair . Mesmo nesse caso, a antecipação é positiva (porque inão corresponde) e é seguida por outro token. Mais uma vez, qcorresponde q e inão corresponde u . A correspondência do lookahead deve ser descartada, de forma que o motor recue uda string para u . A antecipação foi bem-sucedida, então o motor continua com u. E uvai combinar com você . Portanto, esta tentativa de correspondência foi bem-sucedida. qu é compatível

Portanto, em conclusão, a diferença real entre grupos de antevisão e de não captura é se você deseja apenas testar a existência ou testar e salvar a correspondência. Capturar grupo é caro, então use-o com cautela.

Freedev
fonte
> então o motor passa de i na string para u. A antecipação foi bem-sucedida, então o mecanismo continua com i. Mas eu não consigo igualar você. ISSO é totalmente confuso. Por que recuar se isso é uma visão antecipada ?
Verde
1
@Green Uma coisa importante para entender sobre lookahead e outras construções lookaround é que embora eles façam os movimentos para ver se sua subexpressão é capaz de corresponder, eles na verdade não “consomem” nenhum texto. Isso pode ser um pouco confuso
freedev
7

Tente comparar foobarcom estes:

/foo(?=b)(.*)/
/foo(?!b)(.*)/

A primeira regex corresponderá e retornará "bar" como primeira submatch - (?=b)corresponde ao 'b', mas não o consome, deixando-o para os parênteses seguintes.

A segunda regex NÃO corresponderá, porque ela espera que "foo" seja seguido por algo diferente de 'b'.

(?:...)tem exatamente o mesmo efeito que simples (...), mas não retorna essa parte como uma submatch.

Lanzz
fonte
0

A maneira mais simples de entender as asserções é tratá-las como o comando inserido em uma expressão regular. Quando o mecanismo funciona para uma asserção, ele verifica imediatamente a condição descrita pela asserção. Se o resultado for verdadeiro, continue a executar a expressão regular.

BlackGlory
fonte
0

Esta é a verdadeira diferença:

>>> re.match('a(?=b)bc', 'abc')
<Match...>
>>> re.match('a(?:b)c', 'abc')
<Match...>

# note:
>>> re.match('a(?=b)c', 'abc')
None

Se você não se importa, o conteúdo após "?:" Ou "? =", "?:" E "? =" São exatamente os mesmos. Ambos podem ser usados.

Mas se você precisar desse conteúdo para um processo posterior (não apenas corresponder a tudo. Nesse caso, você pode simplesmente usar "a (b)"). Você deve usar "? =". Porque "?:" Vai simplesmente passar.

Bebedor de chá
fonte