2012-07-03 12 views
5

¿Existe un método API que devuelva todas las subcadenas (posiblemente superpuestas) que coincidan con una expresión regular?Todas las subcadenas superpuestas que coincidan con una expresión regular java

Por ejemplo, tengo una cadena de texto: String t = 04/31 412-555-1235;, y tengo un patrón: Pattern p = new Pattern("\\d\\d+"); que coincide con cadenas de dos o más caracteres.

Los partidos que recibo son: 04, 31, 412, 555, 1235.

¿Cómo consigo la superposición de partidos?

Quiero el código para volver: 04, 31, 41, 412, 12, 55, 555, 55, 12, 123, 1235, 23, 235, 35.

En teoría debería ser posible - hay un algoritmo obvio O(n^2) que enumera y comprueba todas las subcadenas contra el patrón.

EDITAR

En lugar de enumerar todas las subcadenas, es más seguro utilizar el método region(int start, int end) en Matcher. Verificar el patrón contra una subcadena separada y extraída puede cambiar el resultado de la coincidencia (por ejemplo, si hay un grupo no captor o una verificación de límite de palabras al inicio/final del patrón).

EDIT 2

En realidad, no está claro si region() hace lo que se puede esperar de partidos de ancho cero. La especificación es vaga y los experimentos arrojan resultados decepcionantes.

Por ejemplo:

String line = "xx90xx"; 
String pat = "\\b90\\b"; 
System.out.println(Pattern.compile(pat).matcher(line).find()); // prints false 
for (int i = 0; i < line.length(); ++i) { 
    for (int j = i + 1; j <= line.length(); ++j) { 
    Matcher m = Pattern.compile(pat).matcher(line).region(i, j); 
    if (m.find() && m.group().size == (j - i)) { 
     System.out.println(m.group() + " (" + i + ", " + j + ")"); // prints 90 (2, 4) 
    } 
    } 
} 

No estoy seguro de cuál es la solución más elegante es. Un enfoque sería tomar una subcadena de line y rellenar con los caracteres de límite apropiados antes de verificar si el pat coincide.

EDITAR 3

Aquí está la solución completa que se me ocurrió. Puede manejar patrones de ancho cero, límites, etc. en la expresión regular original. Examina todas las subcadenas de la cadena de texto y comprueba si la expresión regular coincide solo en la posición específica al rellenar el patrón con el número apropiado de comodines al principio y al final. Parece que funciona para los casos que probé, aunque no he hecho pruebas exhaustivas. Sin duda es menos eficiente de lo que podría ser.

public static void allMatches(String text, String regex) 
    { 
    for (int i = 0; i < text.length(); ++i) { 
     for (int j = i + 1; j <= text.length(); ++j) { 
     String positionSpecificPattern = "((?<=^.{"+i+"})("+regex+")(?=.{"+(text.length() - j)+"}$))"; 
     Matcher m = Pattern.compile(positionSpecificPattern).matcher(text); 

     if (m.find()) 
     { 
      System.out.println("Match found: \"" + (m.group()) + "\" at position [" + i + ", " + j + ")"); 
     } 
     } 
    } 
    } 

EDITAR 4

Aquí hay una mejor forma de hacer esto: https://stackoverflow.com/a/11372670/244526

EDITAR 5

La biblioteca JRegex apoya la búsqueda de todas las subcadenas superpuestas que coincidan con una expresión regular de Java (aunque parece que no se ha actualizado en un momento).En concreto, el documentation on non-breaking search especifica:

Uso de la búsqueda de no separación se puede encontrar todas las posibles occureneces de un patrón , incluyendo aquellos que se cruzan o anidado. Esto es logrado mediante el método de Matcher proceda() en lugar de encontrar()

+0

acaba de hacer un bucle post-regex a través de los 3 o más resultados de caracteres –

+0

http://regexlib.com/ podría ser un buen lugar para hacer algunas excavaciones. –

+0

@ Ωmega Hago todo lo posible, pero estoy abierto a comentarios que no son útiles. Aclamaciones. –

Respuesta

0

Lo más cercano que puede obtener es algo como esto.

"(?=((\\d*)\\d))(?=(\\d)\\d*)" 

El resultado será en la captura de grupo 1, 2 y 3.

En lo que a mi imaginación puede ir, sólo puede pensar en la captura de longitud cero afirmación como un camino viable para recuperar la misma posición de una cuerda. Capturar texto fuera de la aserción de longitud cero consumirá el texto de una vez y para siempre (look-behind solo puede capturar longitud fija en Java, por lo que puede considerarse inaccesible).

Esta solución no es perfecta: aparte de la repetición (del texto en la misma posición) y las cadenas vacías coinciden, no capturará todas las subcadenas posibles.

Una forma de capturar todas las subcadenas posibles es construir la siguiente expresión regular con el valor de n a partir de 1:

"(?=(\\d{" + n + "}))" 

Y coincide con la cadena contra este para incrementar el valor de n hasta que no hay ninguna coincidencia.

Este método es, por supuesto, ineficiente en comparación con el método de emparejar todos los números con "\ d +" y extraer toda la subcadena.

0

Es factible como O (n)sólo si se especifica el intervalo de longitud permitida número.

Digamos que a partir de 2-4 dígitos (números 00-9999): (?=(\\d{2}))(?=(\\1\\d)?)(?=(\\2\\d)?)

Esta es una afirmación de longitud cero a través de búsqueda positiva hacia delante, capturando tales lookahead en grupos. El resultado es una matriz de todas las cadenas de 2 a 4 dígitos que se pueden encontrar dentro de la entrada de expresiones regulares, junto con cadenas duplicadas y vacías (para capturas sin correspondencia).

No soy un desarrollador de Java, pero creo que también se puede leer un script de Perl como ejemplo.

#!/usr/bin/perl          # perl script 
use List::MoreUtils qw/ uniq /;      # uniq subroutine library 
$_ = '04/31 412-555-1235';       # input 
my @n = uniq (/(?=(\d{2}))(?=(\1\d)?)(?=(\2\d)?)/g); # regex (single slash in Perl) 
print "$_\n" for grep(/\S/, @n);      # print non-empty lines 

El truco está en utilizar las referencias retrospectivas. Si desea capturar cadenas de 2 a 5 dígitos, deberá usar una búsqueda anticipada más positiva en la expresión regular: (?=(\\d{2}))(?=(\\1\\d)?)(?=(\\2\\d)?)(?=(\\3\\d)?).

Creo que este es un enfoque más cercano que puede hacer. Si esto funciona para usted, deje un comentario y espero que algún desarrollador de Java edite mi respuesta con código Java para el script anterior.

+0

La expresión regular es lo mismo en Java (excepto que la barra invertida necesita ser escapada). En cuanto a 'uniq', se puede simular con' Set' en Java ('TreeSet' o' HashSet'). – nhahtdh

+0

@nhahtdh - Gracias. Siéntase libre de agregar una actualización a mi respuesta editando la publicación. –

1

Me enfrenté a una situación similar e intenté las respuestas anteriores, pero en mi caso tomó demasiado tiempo establecer el índice de inicio y final del marcador , pero creo que encontré una mejor solución, estoy publicarlo aquí para otros. Así que abajo está mi sniplet de código.

if (textToParse != null) { 
Matcher matcher = PLACEHOLDER_PATTERN.matcher(textToParse); 
    while(matcher.hitEnd()!=true){ 
     Boolean result = matcher.find(); 
     int count = matcher.groupCount(); 
     System.out.println("Result " +result+" count "+count); 
     if(result==true && count==1){ 
      mergeFieldName = matcher.group(1); 
      mergeFieldNames.add(mergeFieldName); 
      } 
     } 
    } 

He utilizado el método matcher.hitEnd() para comprobar si he llegado al final del texto.

Espero que esto ayude. Gracias!

Cuestiones relacionadas