Cuál es la diferencia entre ?:, ?! y? = en regex?

106

Busqué el significado de estas expresiones pero no pude entender la diferencia exacta entre ellas. Esto es lo que ellos dicen:

  • ?: Haga coincidir la expresión pero no la capture.
  • ?= Coincidir con un sufijo pero excluirlo de la captura.
  • ?! Coincidir si el sufijo está ausente.

Intenté usarlos en RegEx simple y obtuve resultados similares para todos. ejemplo: las siguientes 3 expresiones dan resultados muy similares.

  • [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
fuente
Muéstranos tu caso de prueba. No deberían dar los mismos resultados.
Bergi
@ sepp2k, tiene resultados similares en algunos casos, uno de ellos mencionado en la pregunta.
RK Poddar
@Bergi, lo probé con datos aleatorios, que contienen palabras en inglés, números de teléfono, direcciones URL, direcciones de correo electrónico, números, etc.
RK Poddar
4
@RKAgarwal Ah, veo lo que hiciste allí. Agregó un *después de los grupos, por lo que simplemente se ignoran.
sepp2k
nota de novato : solo los usaría al comienzo del paréntesis, y los paréntesis forman un grupo de captura (diferentes conjuntos de paréntesis extraen diferentes secciones de texto).
Ryan Taylor

Respuestas:

150

La diferencia entre ?=y ?!es que el primero requiere que la expresión dada coincida y el segundo requiere que no coincida. Por ejemplo a(?=b), coincidirá con la "a" en "ab", pero no con la "a" en "ac". Considerando a(?!b)que coincidirá con la "a" en "ac", pero no con la "a" en "ab".

La diferencia entre ?:y ?=es que ?=excluye la expresión de toda la coincidencia, mientras que ?:simplemente no crea un grupo de captura. Entonces, por ejemplo a(?:b), coincidirá con "ab" en "abc", mientras a(?=b)que solo coincidirá con "a" en "abc". a(b)coincidiría con la "ab" en "abc" y crearía una captura que contenga la "b".

sepp2k
fuente
78
?:  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

Consulte aquí: http://www.regular-expressions.info/lookaround.html para obtener muy buenos tutoriales y ejemplos de anticipación en expresiones regulares.

anubhava
fuente
15
Sin embargo, JavaScript no sabe mirar atrás.
Bergi
1
Este es más completo para expresiones regulares generales.
Yan Yang
/ (? <= ^ a) b / ¡funcionó para mí en javascript! Parece que no hay un tutorial para buscar detrás de Javascript en Internet.
Y. Yoshii
Solo las versiones recientes de los navegadores han comenzado a admitir mirar atrás en JS
anubhava
- anubhava No conozco ninguna alternativa a / (? <= ^ A) b / usando la expresión regular pura. Quizás pueda, pero tendría que depender de las funciones de devolución de llamada.
Y. Yoshii
21

Para comprender mejor, apliquemos las tres expresiones más un grupo de captura y analicemos cada comportamiento.

  • () grupo de captura : la expresión regular dentro del paréntesis debe coincidir y la coincidencia debe crear un grupo de captura
  • (?:) grupo sin captura : la expresión regular dentro del paréntesis debe coincidir pero no crea el grupo de captura
  • (?=) mirada positiva hacia el futuro : afirma que la expresión regular debe coincidir
  • (?!) mirada negativa hacia adelante : afirma que es imposible hacer coincidir la expresión regular

Solicitemos q(u)ipara dejar de fumar . qcoincide con q y el grupo de captura ucoincide con u . Se toma la coincidencia dentro del grupo de captura y se crea un grupo de captura. Entonces el motor continúa con i. Y icoincidirá con i . Este último intento de coincidencia tiene éxito. qui se empareja y se crea un grupo de captura con u .

Solicitemos q(?:u)ipara dejar de fumar . Nuevamente, qcoincide con q y el grupo que no captura ucoincide con u . Se toma la coincidencia del grupo de no captura, pero no se crea el grupo de captura. Entonces el motor continúa con i. Y icoincidirá con i . Este último intento de coincidencia tiene éxito. qui está emparejado

Solicitemos q(?=u)ipara dejar de fumar . La anticipación es positiva y va seguida de otra ficha. Nuevamente, qcoincide con q y ucoincide con u . Nuevamente, la coincidencia de la búsqueda anticipada debe descartarse, por lo que el motor retrocede desde ila cadena hacia u . La búsqueda anticipada se realizó correctamente, por lo que el motor continúa con i. Pero ino puede coincidir con u . Entonces este intento de partido falla.

Solicitemos q(?=u)upara dejar de fumar . La anticipación es positiva y va seguida de otra ficha. Nuevamente, qcoincide con q y ucoincide con u . La coincidencia de la búsqueda anticipada debe descartarse, por lo que el motor retrocede desde ula cadena hacia u . La búsqueda anticipada se realizó correctamente, por lo que el motor continúa con u. Y ucoincidirá con usted . Entonces este intento de emparejamiento es exitoso. qu está emparejado

Solicitemos q(?!i)upara dejar de fumar . Incluso en este caso, la búsqueda anticipada es positiva (porque ino coincide) y va seguida de otro token. Nuevamente, qcoincide con q y ino coincide con u . La coincidencia de la búsqueda anticipada debe descartarse, por lo que el motor retrocede desde ula cadena hacia u . La búsqueda anticipada se realizó correctamente, por lo que el motor continúa con u. Y ucoincidirá con usted . Entonces este intento de emparejamiento es exitoso. qu está emparejado

Entonces, en conclusión, la diferencia real entre los grupos de búsqueda anticipada y los que no capturan se trata de si solo desea probar la existencia o probar y guardar la coincidencia. Los grupos de captura son costosos, así que úselo con prudencia.

freedev
fuente
> para que el motor retroceda de i en la cadena a u. La búsqueda anticipada fue exitosa, por lo que el motor continúa con i. Pero no puedo igualarlo. ESTO es totalmente confuso. ¿Por qué volver paso es si esta búsqueda hacia delante ?
Verde
1
@Green Una cosa importante para entender acerca de las construcciones de lookahead y otros lookaround es que, aunque siguen los movimientos para ver si su subexpresión es capaz de coincidir, en realidad no “consumen” ningún texto. Eso puede ser un poco confuso
freedev
7

Intente hacer coincidir foobarcon estos:

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

La primera expresión regular coincidirá y devolverá "barra" como primera subcoincidencia - (?=b) coincide con la 'b', pero no la consume, dejándola para los siguientes paréntesis.

La segunda expresión regular NO coincidirá, porque espera que "foo" sea seguido por algo diferente de 'b'.

(?:...)tiene exactamente el mismo efecto que simple (...), pero no devuelve esa parte como subcoincidencia.

lanzz
fuente
0

La forma más sencilla de entender las afirmaciones es tratarlas como el comando insertado en una expresión regular. Cuando el motor funciona a una afirmación, verificará inmediatamente la condición descrita por la afirmación. Si el resultado es verdadero, continúe ejecutando la expresión regular.

BlackGlory
fuente
0

Esta es la verdadera diferencia:

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

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

Si no le importa el contenido después de "?:" O "? =", "?:" Y "? =" Son iguales. Ambos están bien para usar.

Pero si necesita ese contenido para un proceso posterior (no solo para que coincida con todo. En ese caso, simplemente puede usar "a (b)") Tiene que usar "? =" En su lugar. Porque "?:" Simplemente lo superará.

TeaDrinker
fuente