¿Cómo funcionan (* SKIP) o (* F) en expresiones regex?

Estoy aprendiendo un uso avanzado de expresiones regulares y noté que muchas publicaciones usan (*SKIP) o (*F) en él.

Publiqué una pregunta donde la idea era hacer coincidir las líneas que no tienen yellow pero que tienen blue solo si el brown existe después del azul. Y la respuesta correcta fue:

 .*yellow.*(*SKIP)(*F)|^.*\bblue\b(?=.*brown).*$ 

También probé expresiones de lookaround como las siguientes, pero no funcionó en todos los casos:

 ^((?!yellow).)*blue(?=.*brown).*$ 

No tenía idea de estas banderas (*SKIP)(*F) , así que la pregunta es, ¿cómo funciona esta bandera? ¿Qué hacen? ¿Y hay otras banderas como estas?

Gracias.

Estos dos verbos de control de retroceso se implementan solo en Perl, PCRE y el módulo pypi regex .

La idea del truco (*SKIP)(*FAIL) es consumir los caracteres que desea evitar, y eso no debe ser parte del resultado del partido.

Un patrón clásico que utiliza este truco se ve así:

 What_I_want_to_avoid(*SKIP)(*FAIL)|What_I_want_to_match 

Un motor de expresiones regulares procesa una cadena como esa:

  • el primer token del patrón se prueba en cada carácter de izquierda a derecha (por defecto la mayor parte del tiempo, pero algunos motores regex se pueden configurar para que funcionen de derecha a izquierda, .net puede hacerlo si recuerdo bien)

  • si el primer token coincide, entonces el motor de expresiones regulares prueba el siguiente token del patrón con los siguientes caracteres (después de la primera coincidencia de token), etc.

  • cuando falla un token, el motor de expresiones regulares obtiene los caracteres que coinciden con el último token e intenta de otra manera hacer que el patrón tenga éxito (si no funciona, el motor de expresiones regulares hace lo mismo con el token anterior, etc.)

Cuando el motor de expresiones regulares cumple el verbo (*SKIP) (en este caso, todos los tokens anteriores obviamente han tenido éxito) , no tiene más derecho a volver a todos los tokens anteriores de la izquierda y no tiene más derecho a volver a intentar todo el token. coincide con caracteres con otra twig del patrón o en la siguiente posición en la cadena hasta el último carácter coincidente (incluido) si el patrón falla más adelante a la derecha del verbo (*SKIP) .

El rol de (*FAIL) es forzar al patrón a fallar. Por lo tanto, todos los caracteres coincidentes a la izquierda de (*SKIP) se omiten y el motor de expresiones regulares continúa su trabajo después de estos caracteres.

La única posibilidad para que el patrón tenga éxito en el patrón de ejemplo es que la primera twig falla antes (*SKIP) para permitir que se pruebe la segunda twig.

Puedes encontrar otro tipo de explicación aquí .

Acerca de Java y otros motores de expresiones regulares que no tienen estas dos características

Los verbos de control de retroceso no se implementan en otros motores regex y no hay equivalente.

Sin embargo, puede usar varias formas de hacer lo mismo (para ser más claro, para evitar algo que pueda ser igualado por otra parte del patrón) .

El uso de grupos de captura:

camino 1:

 What_I_want_to_avoid|(What_I_want_to_match) 

Solo necesita extraer el grupo de captura 1 (o para probar si existe) , ya que es lo que está buscando. Si usa el patrón para realizar un reemplazo, puede usar las propiedades del resultado del partido (desplazamiento, longitud, grupo de captura) para realizar el reemplazo con las funciones de cuerda clásicas. Otro lenguaje como javascript, ruby ​​… permite usar una función de callback como reemplazo.

camino 2:

 ((?>To_avoid|Other_things_that_can_be_before_what_i_want)*)(What_I_want) 

Es la manera más fácil para el reemplazo, sin necesidad de callback, la cadena de reemplazo solo necesita comenzar con \1 (o $1 )

El uso de lookarounds:

Por ejemplo, quiere encontrar una palabra que no esté incrustada entre otras dos palabras (digamos S_word y E_word que sean diferentes (vea el comentario de Qtax) ):

(los casos de borde S_word E_word word E_word y S_word word S_word E_word se permiten en este ejemplo).

La forma del verbo de control de retroceso será:

 S_word not_S_word_or_E_word E_word(*SKIP)(*F)|word 

Para usarlo de esta forma, el motor de expresiones regulares debe permitir cierto scope de longitud variable en cierta medida. Con .net o el nuevo módulo regex, sin problemas, lookbehinds puede tener una longitud totalmente variable. También es posible con Java, pero el tamaño debe ser limitado (por ejemplo: (?<=.{1,1000}) ) .

El equivalente de Java será:

 word(?:(?!not_S_word_or_E_word E_word)|(? 

Tenga en cuenta que, en algunos casos, solo es necesaria la búsqueda anticipada. Tenga en cuenta también que comenzar un patrón con carácter literal es más eficiente que comenzar con un lookbehind, es por eso que lo puse después de la palabra (incluso si tengo que volver a escribir la palabra una vez más en la afirmación).

Los patrones (*SKIP) y (*F) (también conocido como *FAIL ) están documentados en el manual de Perl: http://perldoc.perl.org/perlre.html

Sin embargo, solo están disponibles en Perl y en sabores de expresiones regulares que imitan Perl (por ejemplo, la biblioteca PCRE utilizada por PHP).

El motor regex incorporado de Java no admite estas extensiones, y no conozco ninguna que lo haga.

Mi consejo general en Java es mantener sus expresiones regulares simples, y usar otros métodos de manipulación de cadenas para lograr lo que no se puede hacer claramente con una expresión regular corta.