2009-10-08 8 views
24

Siempre pensé que una aserción look-behind en la API regex de Java (y muchos otros lenguajes para el caso) debe tener una longitud obvia. Por lo tanto, los cuantificadores STAR y PLUS no están permitidos dentro de miradas atrás.Regex look-behind sin la longitud máxima obvia en Java

La excelente recurso en línea regular-expressions.info parece confirmar (algunos de) mis suposiciones:

"[...] Java lleva las cosas un paso más allá, permitiendo la repetición finita Todavía no puede utilizar la estrella. o más, pero puede utilizar el signo de interrogación y los llaves con el parámetro máximo especificado. Java reconoce el hecho de que la repetición finita puede ser reescribirse como una alternancia de cadenas con diferente, pero longitudes fijas. Desafortunadamente, el JDK 1.4 y el 1.5 tienen algunos errores cuando usa la alternancia dentro de lookbehind. Estos se corrigieron en JDK 1.6. [...]"

- http://www.regular-expressions.info/lookaround.html

Usando las llaves funciona siempre y cuando la longitud total del rango de los personajes dentro de la mirada detrás es menor o igual a Integer.MAX_VALUE Así. estas expresiones regulares son válidas:

"(?<=a{0," +(Integer.MAX_VALUE) + "})B" 
"(?<=Ca{0," +(Integer.MAX_VALUE-1) + "})B" 
"(?<=CCa{0," +(Integer.MAX_VALUE-2) + "})B" 

Pero estos no son los siguientes:

"(?<=Ca{0," +(Integer.MAX_VALUE) +"})B" 
"(?<=CCa{0," +(Integer.MAX_VALUE-1) +"})B" 

sin embargo, no entiendo º e siguiente:

Cuando corro una prueba usando el cuantificador + * y dentro de un mirada detrás, todo va bien (ver la salida Prueba 1 y Prueba 2).

Pero, cuando agrego un solo carácter en el inicio de la mirada- detrás de Prueba 1 y Prueba 2, se rompe (ver la salida Prueba 3).

Hacer el * codiciosos de Prueba 3 reacios no tiene ningún efecto, todavía se rompe (ver ensayo 4).

Aquí está el instrumento de prueba:

public class Main { 

    private static String testFind(String regex, String input) { 
     try { 
      boolean returned = java.util.regex.Pattern.compile(regex).matcher(input).find(); 
      return "testFind  : Valid -> regex = "+regex+", input = "+input+", returned = "+returned; 
     } catch(Exception e) { 
      return "testFind  : Invalid -> "+regex+", "+e.getMessage(); 
     } 
    } 

    private static String testReplaceAll(String regex, String input) { 
     try { 
      String returned = input.replaceAll(regex, "FOO"); 
      return "testReplaceAll : Valid -> regex = "+regex+", input = "+input+", returned = "+returned; 
     } catch(Exception e) { 
      return "testReplaceAll : Invalid -> "+regex+", "+e.getMessage(); 
     } 
    } 

    private static String testSplit(String regex, String input) { 
     try { 
      String[] returned = input.split(regex); 
      return "testSplit  : Valid -> regex = "+regex+", input = "+input+", returned = "+java.util.Arrays.toString(returned); 
     } catch(Exception e) { 
      return "testSplit  : Invalid -> "+regex+", "+e.getMessage(); 
     } 
    } 

    public static void main(String[] args) { 
     String[] regexes = {"(?<=a*)B", "(?<=a+)B", "(?<=Ca*)B", "(?<=Ca*?)B"}; 
     String input = "CaaaaaaaaaaaaaaaBaaaa"; 
     int test = 0; 
     for(String regex : regexes) { 
      test++; 
      System.out.println("********************** Test "+test+" **********************"); 
      System.out.println(" "+testFind(regex, input)); 
      System.out.println(" "+testReplaceAll(regex, input)); 
      System.out.println(" "+testSplit(regex, input)); 
      System.out.println(); 
     } 
    } 
} 

La salida:

********************** Test 1 ********************** 
    testFind  : Valid -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = true 
    testReplaceAll : Valid -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = CaaaaaaaaaaaaaaaFOOaaaa 
    testSplit  : Valid -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = [Caaaaaaaaaaaaaaa, aaaa] 

********************** Test 2 ********************** 
    testFind  : Valid -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = true 
    testReplaceAll : Valid -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = CaaaaaaaaaaaaaaaFOOaaaa 
    testSplit  : Valid -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = [Caaaaaaaaaaaaaaa, aaaa] 

********************** Test 3 ********************** 
    testFind  : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6 
(?<=Ca*)B 
    ^
    testReplaceAll : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6 
(?<=Ca*)B 
    ^
    testSplit  : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6 
(?<=Ca*)B 
    ^

********************** Test 4 ********************** 
    testFind  : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7 
(?<=Ca*?)B 
    ^
    testReplaceAll : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7 
(?<=Ca*?)B 
    ^
    testSplit  : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7 
(?<=Ca*?)B 
    ^

Mi pregunta puede ser obvio, pero todavía me pida que: Puede alguien explicarme por qué Prueba 1 y error y Prueba 3 y ¿no? Hubiera esperado que fallaran todos, no la mitad de ellos funcionaban y la mitad fallaron.

Gracias.

PS. Estoy usando: Java versión 1.6.0_14

Respuesta

17

Al echar un vistazo al código fuente de Pattern.java, se revela que el '*' y '+' se implementan como instancias de Curly (que es el objeto creado para operadores rizados). Así,

a* 

se implementa como

a{0,0x7FFFFFFF} 

y

a+ 

se implementa como

a{1,0x7FFFFFFF} 

que es por eso que ver exactamente los mismos comportamientos para curlies y estrellas .

+0

No se me ocurrió mirar la fuente de Pattern ... Silly me. ¡Gracias! Tiene mucho sentido ahora. –

+0

Una de las muchas razones por las que no puedo vivir sin Eclipse es mi dedo cíclico con picazón (que, si no usas Eclipse, significa "abrir el archivo de origen donde está definido este nombre"). –

+0

Gracias, nunca me tomé la molestia de conectar la fuente a Eclipse. Lo haré ahora seguro. Gracias. –

13

Es un error: http://bugs.sun.com/view_bug.do?bug_id=6695369

Pattern.compile() se supone siempre una excepción si no se puede determinar la longitud máxima posible de un partido de búsqueda hacia atrás.

+0

¡Hola Alan, pensé que vendrías pronto! Gracias, sabía que esto solía ser un error (en 1.5), pero recordé que el motor siempre evaluaba a falso o algo así. Ahora evalúa el resultado correctamente, así que pensé que el error se había solucionado en 1.6. Debo haber recordado incorrectamente. Gracias por la info. Bart (aka prometheuzz) –

+0

Ese error se corrigió; este es uno nuevo que se introdujo en 1.6. :/ –

+0

Ah, genial: |. Gracias por la info. –

Cuestiones relacionadas