2011-05-16 19 views
109

En Java, intento devolver todas las coincidencias de expresiones regulares a una matriz, pero parece que solo se puede verificar si el patrón coincide con algo o no (booleano). ¿Alguien puede ayudarme a usar una coincidencia de expresiones regulares para formar una matriz de todas las cadenas que coincidan con una expresión de expresiones regulares en una cadena dada? Gracias!Crear una matriz de coincidencias de expresiones regulares

+2

Buena pregunta. La información que busca debe ser parte de los documentos de Java en Regex y Matcher. Lamentablemente, no lo es. – Cheeso

+2

Una verdadera lástima.Esta funcionalidad parece existir fuera de la caja en casi todos los demás idiomas (que tiene soporte para expresiones regulares). –

Respuesta

192

(4castle's answer es mejor que el de abajo si se puede asumir Java> = 9)

es necesario crear un matcher y el uso que de forma iterativa para encontrar coincidencias.

import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

... 

List<String> allMatches = new ArrayList<String>(); 
Matcher m = Pattern.compile("your regular expression here") 
    .matcher(yourStringHere); 
while (m.find()) { 
    allMatches.add(m.group()); 
} 

Después de esto, allMatches contiene las coincidencias, y se puede utilizar allMatches.toArray(new String[0]) para obtener una matriz si realmente lo necesita.


También se puede utilizar para escribir MatchResult funciones de ayuda para recorrer partidos desde Matcher.toMatchResult() devuelve una instantánea del estado actual grupo.

Por ejemplo, puede escribir un iterador perezosa para permitirle hacer

for (MatchResult match : allMatches(pattern, input)) { 
    // Use match, and maybe break without doing the work to find all possible matches. 
} 

haciendo algo como esto:

public static Iterable<MatchResult> allMatches(
     final Pattern p, final CharSequence input) { 
    return new Iterable<MatchResult>() { 
    public Iterator<MatchResult> iterator() { 
     return new Iterator<MatchResult>() { 
     // Use a matcher internally. 
     final Matcher matcher = p.matcher(input); 
     // Keep a match around that supports any interleaving of hasNext/next calls. 
     MatchResult pending; 

     public boolean hasNext() { 
      // Lazily fill pending, and avoid calling find() multiple times if the 
      // clients call hasNext() repeatedly before sampling via next(). 
      if (pending == null && matcher.find()) { 
      pending = matcher.toMatchResult(); 
      } 
      return pending != null; 
     } 

     public MatchResult next() { 
      // Fill pending if necessary (as when clients call next() without 
      // checking hasNext()), throw if not possible. 
      if (!hasNext()) { throw new NoSuchElementException(); } 
      // Consume pending so next call to hasNext() does a find(). 
      MatchResult next = pending; 
      pending = null; 
      return next; 
     } 

     /** Required to satisfy the interface, but unsupported. */ 
     public void remove() { throw new UnsupportedOperationException(); } 
     }; 
    } 
    }; 
} 

Con esto,

for (MatchResult match : allMatches(Pattern.compile("[abc]"), "abracadabra")) { 
    System.out.println(match.group() + " at " + match.start()); 
} 

rendimientos

a at 0 
b at 1 
a at 3 
c at 4 
a at 5 
a at 7 
b at 8 
a at 10 
+2

No recomendaría el uso de una ArrayList aquí, ya que no conoce el tamaño por adelantado y podría querer evitar el cambio de tamaño del búfer. En cambio, preferiría una LinkedList, aunque es solo una sugerencia y no hace que tu respuesta sea menos válida en absoluto. – Liv

+8

@Liv, tómese el tiempo para comparar tanto 'ArrayList' como' LinkedList'; los resultados pueden ser sorprendentes. –

+0

Escucho lo que dice y soy consciente de la velocidad de ejecución y la huella de memoria en ambos casos; el problema con ArrayList es que el constructor predeterminado crea una capacidad de 10, si pasa ese tamaño con llamadas para agregar() tendrá que soportar la asignación de memoria y la copia de matriz, y eso puede suceder varias veces. Por supuesto, si espera unas pocas coincidencias, su enfoque es el más eficiente; Sin embargo, si encuentra que el "cambio de tamaño" de la matriz ocurre más de una vez, sugeriría una lista enlazada, incluso más si se trata de una aplicación de baja latencia. – Liv

8

Aquí está un ejemplo sencillo:

Pattern pattern = Pattern.compile(regexPattern); 
List<String> list = new ArrayList<String>(); 
Matcher m = pattern.matcher(input); 
while (m.find()) { 
    list.add(m.group()); 
} 

(si tiene más grupos de captura, puede hacer referencia a ellos por su índice como un argumento del método de grupos Si necesita una matriz, a continuación, utilizar list.toArray().)

+0

pattern.matches (entrada) no funciona. Tienes que pasar tu patrón de expresiones regulares (otra vez!) -> WTF Java ?! pattern.matches (String regex, String input); ¿Te refieres a pattern.matcher (entrada)? –

+0

@ElMac ['Pattern.matches()'] (http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#matches-java.lang.String-java .lang.CharSequence-) es un método estático, no deberías invocarlo en una instancia 'Pattern'. 'Pattern.matches (regex, input)' es simplemente una abreviatura de 'Pattern.compile (regex) .matcher (input) .matches()'. – dimo414

5

Desde el Official Regex Java Trails:

 Pattern pattern = 
     Pattern.compile(console.readLine("%nEnter your regex: ")); 

     Matcher matcher = 
     pattern.matcher(console.readLine("Enter input string to search: ")); 

     boolean found = false; 
     while (matcher.find()) { 
      console.format("I found the text \"%s\" starting at " + 
       "index %d and ending at index %d.%n", 
       matcher.group(), matcher.start(), matcher.end()); 
      found = true; 
     } 

Uso find e inserte el group resultante a la matriz/Lista/lo que sea.

25

Java hace que la expresión regular sea demasiado complicada y no sigue el estilo perl. Echar un vistazo a MentaRegex para ver cómo se puede lograr eso en una sola línea de código Java:

String[] matches = match("aa11bb22", "/(\\d+)/g"); // => ["11", "22"] 
+4

Eso es genial. La barra doble todavía se ve fea, pero creo que no hay escapatoria de eso. – JohnPristine

+0

mentaregex-0.9.5.jar, 6Kb que salvó mi día, Obrigado Sérgio! –

+2

¡ATENCIÓN! La mejor solucion. Úselo! –

-1
 Set<String> keyList = new HashSet(); 
     Pattern regex = Pattern.compile("#\\{(.*?)\\}"); 
     Matcher matcher = regex.matcher("Content goes here"); 
     while(matcher.find()) { 
      keyList.add(matcher.group(1)); 
     } 
     return keyList; 
9

En Java 9, ahora se puede utilizar Matcher#results() para obtener una Stream<MatchResult> que se puede utilizar para obtener una lista/matriz de coincidencias.

import java.util.regex.Pattern; 
import java.util.regex.MatchResult; 
String[] matches = Pattern.compile("your regex here") 
          .matcher("string to search from here") 
          .results() 
          .map(MatchResult::group) 
          .toArray(String[]::new); 
        // or .collect(Collectors.toList()) 
Cuestiones relacionadas