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]+)*

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".


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.


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.


> 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.


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.


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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.