¿Qué hace (?! A) {0}? Significa en un regex de Java?

Inspirado por la pregunta de si el cuantificador {0} realmente tiene sentido , empecé a jugar con algunas expresiones regulares que contenían {0} cuantificador y escribí este pequeño programa java que sólo divide una frase de prueba basada en varios regex de prueba:

 private static final String TEST_STR = "Just a test-phrase!! 1.2.3.. @ {(t·e·s·t)}"; private static void test(final String pattern) { System.out.format("%-17s", "\"" + pattern + "\":"); System.out.println(Arrays.toString(TEST_STR.split(pattern))); } public static void main(String[] args) { test(""); test("{0}"); test(".{0}"); test("([^.]{0})?+"); test("(?!a){0}"); test("(?!a).{0}"); test("(?!.{0}).{0}"); test(".{0}(?<!a)"); test(".{0}(?<!.{0})"); } 

==> La salida:

 "": [, J, u, s, t, , a, , t, e, s, t, -, p, h, r, a, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }] "{0}": [, J, u, s, t, , a, , t, e, s, t, -, p, h, r, a, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }] ".{0}": [, J, u, s, t, , a, , t, e, s, t, -, p, h, r, a, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }] "([^.]{0})?+": [, J, u, s, t, , a, , t, e, s, t, -, p, h, r, a, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }] "(?!a){0}": [, J, u, s, t, , a, , t, e, s, t, -, p, h, r, a, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }] "(?!a).{0}": [, J, u, s, t, a, , t, e, s, t, -, p, h, ra, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }] "(?!.{0}).{0}": [Just a test-phrase!! 1.2.3.. @ {(t·e·s·t)}] ".{0}(?<!a)": [, J, u, s, t, , a , t, e, s, t, -, p, h, r, as, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }] ".{0}(?<!.{0})": [Just a test-phrase!! 1.2.3.. @ {(t·e·s·t)}] 

Lo siguiente no me sorprendió :

  1. "" , ".{0}" , y "([^.]{0})?+" Acaba de dividir antes de cada carácter y que tiene sentido debido a 0-cuantificador.
  2. "(?!.{0}).{0}" y ".{0}(?<!.{0})" no coinciden con nada. Tiene sentido para mí: Negative Lookahead / Lookbehind para 0-token cuantificado no coincidirá.

Lo que me sorprendió :

  1. En realidad, esperaba una Excepción aquí, debido a que el token anterior no era cuantificable: Para {0} simplemente no hay nada precedente y para (?!a){0} No realmente sólo un lookahead negativo. Ambos coinciden justo antes de cada char, ¿por qué? Si intento que regex en un validador javascript, me sale "error no cuantificable", ver la demostración aquí ! ¿Es eso regex manejado diferentemente en Java y Javascript?
  2. "(?!a).{0}" Una pequeña sorpresa también aquí: Los que coinciden antes de cada char de la frase, excepto antes / después de la a . Mi comprensión es que en (?!a).{0} (?!a) La parte (?!a) Negativa mira a la cabeza que es imposible emparejar el a literalmente, pero estoy mirando hacia delante. Pensé que no funcionaría con 0-cuantificado token, pero parece que puedo usar Lookahead con aquellos también.

==> Así que el misterio restante para mí es por qué (?!a){0} es en realidad coincidencia antes de cada char en mi frase de prueba. ¿No debería ser realmente un patrón no válido y lanzar una PatternSyntaxException o algo así?


Actualizar:

Si ejecuto el mismo código Java dentro de una actividad de Android, ¡ el resultado es diferente ! Allí el regex (?!a){0} realmente lanza una PatternSyntaxException, vea:

 03-20 22:43:31.941: D/AndroidRuntime(2799): Shutting down VM 03-20 22:43:31.950: E/AndroidRuntime(2799): FATAL EXCEPTION: main 03-20 22:43:31.950: E/AndroidRuntime(2799): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.appham.courseraapp1/com.appham.courseraapp1.MainActivity}: java.util.regex.PatternSyntaxException: Syntax error in regexp pattern near index 6: 03-20 22:43:31.950: E/AndroidRuntime(2799): (?!a){0} 03-20 22:43:31.950: E/AndroidRuntime(2799): ^ 03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180) 03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230) 03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread.access$600(ActivityThread.java:141) 03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234) 03-20 22:43:31.950: E/AndroidRuntime(2799): at android.os.Handler.dispatchMessage(Handler.java:99) 03-20 22:43:31.950: E/AndroidRuntime(2799): at android.os.Looper.loop(Looper.java:137) 03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread.main(ActivityThread.java:5041) 03-20 22:43:31.950: E/AndroidRuntime(2799): at java.lang.reflect.Method.invokeNative(Native Method) 03-20 22:43:31.950: E/AndroidRuntime(2799): at java.lang.reflect.Method.invoke(Method.java:511) 03-20 22:43:31.950: E/AndroidRuntime(2799): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) 03-20 22:43:31.950: E/AndroidRuntime(2799): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) 03-20 22:43:31.950: E/AndroidRuntime(2799): at dalvik.system.NativeStart.main(Native Method) 03-20 22:43:31.950: E/AndroidRuntime(2799): Caused by: java.util.regex.PatternSyntaxException: Syntax error in regexp pattern near index 6: 03-20 22:43:31.950: E/AndroidRuntime(2799): (?!a){0} 03-20 22:43:31.950: E/AndroidRuntime(2799): ^ 03-20 22:43:31.950: E/AndroidRuntime(2799): at java.util.regex.Pattern.compileImpl(Native Method) 03-20 22:43:31.950: E/AndroidRuntime(2799): at java.util.regex.Pattern.compile(Pattern.java:407) 03-20 22:43:31.950: E/AndroidRuntime(2799): at java.util.regex.Pattern.<init>(Pattern.java:390) 03-20 22:43:31.950: E/AndroidRuntime(2799): at java.util.regex.Pattern.compile(Pattern.java:381) 03-20 22:43:31.950: E/AndroidRuntime(2799): at java.lang.String.split(String.java:1832) 03-20 22:43:31.950: E/AndroidRuntime(2799): at java.lang.String.split(String.java:1813) 03-20 22:43:31.950: E/AndroidRuntime(2799): at com.appham.courseraapp1.MainActivity.onCreate(MainActivity.java:22) 03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.Activity.performCreate(Activity.java:5104) 03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080) 03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144) 03-20 22:43:31.950: E/AndroidRuntime(2799): ... 11 more 

¿Por qué regex en Android se comporta diferente de Java simple?

One Solution collect form web for “¿Qué hace (?! A) {0}? Significa en un regex de Java?”

Hice algunos mirando a la fuente de oráculos java 1.7.

"{0}"

He encontrado algún código que lanza "Dangling meta carácter" cuando se encuentra?, * O + en el bucle principal. Es decir, no inmediatamente después de algún grupo literal, "." O en cualquier otro lugar donde se comprueban explícitamente los cuantificadores. Por alguna razón, { no está en esa lista. El resultado es que cae en todas las comprobaciones de caracteres especiales y comienza a analizar para una cadena literal. El primer carácter que encuentra es { , que le dice al analizador que es tiempo de detener el análisis de la cadena literal y comprobar los cuantificadores.

El resultado es que "{n}" coincidirá con la cadena vacía n veces.

Otro resultado es que una segunda "x{m}{n}" coincidirá primero con x m veces, y luego hará coincidir la cadena vacía n veces, haciendo caso omiso de la {n} , como mencionó @Kobi en los comentarios anteriores.

Parece un error para mí, pero no me sorprendería si quieren mantenerlo para compatibilidad con versiones anteriores.

"(?!a){0}"

"(?!a)" es sólo un nodo que es cuantificable. Puede comprobar si el siguiente carácter es un 'a' 10 veces. Volverá el mismo resultado cada vez, así que no es muy útil. En nuestro caso, comprobará si el siguiente carácter es 'a' 0 veces, lo que siempre tendrá éxito.

Tenga en cuenta que como una optimización cuando una coincidencia tiene 0 longitud, como aquí, el cuantificador nunca es codicioso. Esto también evita la recursión infinita en el caso "(?!a)*" .

"(?!a).{0}" & ".{0}(?<!a)"

Como se mencionó anteriormente, {0} realiza una comprobación 0 veces, lo que siempre tiene éxito. De hecho, ignora todo lo que viene antes. Eso significa que "(?!a).{0}" es el mismo que "(?!a)" , que tiene el resultado esperado.

Similar para el otro.

Android es diferente

Como se mencionó por @GenericJam, android es una implementación diferente y puede tener características diferentes en estos casos de borde. Intenté mirar esa fuente también, pero androide realmente utiliza código nativo allí 🙂

  • Cómo utilizar la expresión regular en android
  • ReplaceAll no reemplaza string
  • ¿Cómo puedo eliminar el "+" y el código del país de un número de teléfono?
  • Android enlaza sin primer carácter
  • Consulta de SQLiteDatabase de Android con Regex
  • Android Java - String .replaceAll para reemplazar caracteres específicos (regex)
  • Referencia a grupos coincidentes anteriores dentro de un regex
  • Manejo de spoiler BBcode Android
  • Reemplazar todas las letras de una cadena menos la primera y la última en Java
  • División de una cadena en Java lanza PatternSyntaxException
  • ¿Cómo lograr límite de palabras en Sqlite Android?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.