Grupos de captura de expresiones regulares de Java

171

Estoy tratando de entender este bloque de código. En el primero, ¿qué es lo que estamos buscando en la expresión?

Entiendo que es cualquier carácter (0 o más veces *) seguido de cualquier número entre 0 y 9 (una o más veces +) seguido de cualquier carácter (0 o más veces *).

Cuando esto se ejecuta, el resultado es:

Found value: This order was placed for QT3000! OK?
Found value: This order was placed for QT300
Found value: 0

¿Podría alguien pasar esto conmigo?

¿Cuál es la ventaja de usar grupos de captura?

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

public class RegexTut3 {

    public static void main(String args[]) {
        String line = "This order was placed for QT3000! OK?"; 
        String pattern = "(.*)(\\d+)(.*)";

        // Create a Pattern object
        Pattern r = Pattern.compile(pattern);

        // Now create matcher object.
        Matcher m = r.matcher(line);

        if (m.find()) {
            System.out.println("Found value: " + m.group(0));
            System.out.println("Found value: " + m.group(1));
            System.out.println("Found value: " + m.group(2));
        } else {
            System.out.println("NO MATCH");
        }
    }

}
Xivilai
fuente
1
Para insertar una nueva línea, coloque 2 espacios al final de la línea. Más acerca de la sintaxis de Markdown: en.wikipedia.org/wiki/Markdown - Ver también: stackoverflow.com/editing-help
assylias

Respuestas:

249

El problema que tienes es con el tipo de cuantificador. Está utilizando un cuantificador codicioso en su primer grupo (índice 1 - índice 0 representa el todo Pattern), lo que significa que coincidirá tanto como sea posible (y dado que es cualquier personaje, coincidirá con tantos caracteres como haya) para cumplir la condición para los siguientes grupos).

En resumen, su primer grupo .*coincide con cualquier cosa siempre que el siguiente grupo \\d+pueda coincidir con algo (en este caso, el último dígito).

Según el tercer grupo, coincidirá con cualquier cosa después del último dígito.

Si lo cambia a un cuantificador renuente en su primer grupo, obtendrá el resultado, supongo que está esperando, es decir, la parte 3000 .

Tenga en cuenta el signo de interrogación en el 1er grupo.

String line = "This order was placed for QT3000! OK?";
Pattern pattern = Pattern.compile("(.*?)(\\d+)(.*)");
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
    System.out.println("group 1: " + matcher.group(1));
    System.out.println("group 2: " + matcher.group(2));
    System.out.println("group 3: " + matcher.group(3));
}

Salida:

group 1: This order was placed for QT
group 2: 3000
group 3: ! OK?

Más información sobre Java Pattern aquí .

Finalmente, los grupos de captura están delimitados por paréntesis, y proporcionan una forma muy útil de usar referencias de retroceso (entre otras cosas), una vez Patternque coincida con la entrada.

En Java 6, solo se puede hacer referencia a los grupos por su orden (cuidado con los grupos anidados y la sutileza de los pedidos).

En Java 7 es mucho más fácil, ya que puede usar grupos con nombre.

Mena
fuente
¡Gracias! Es la razón por la que el grupo 2 almacenó 0 porque toda la línea fue consumida por el cuantificador codicioso que luego retrocedió hasta que entró en contacto con uno o más números. 0 satisfecho esto por lo que la expresión tuvo éxito. El tercer grupo me parece confuso, ¿ese codificador codicioso también consume toda la línea, pero retrocede hasta que encuentra uno o más números (\\ d +) que se supone que lo preceden?
Xivilai
@Xivilai me permite ajustar mi explicación en mi respuesta, solo un segundo.
Mena
Esa es una buena explicación. Entonces, el renuente comienza desde la izquierda y solo toma el mínimo, mientras que con el codicioso, tomará tanto como sea posible (comenzando desde la derecha), solo parando antes del último dígito para satisfacer esa condición. El tercer grupo toma el resto.
Xivilai
@Xivilai más o menos. Sin embargo, siempre comienza desde la izquierda en este caso. Aquí hay más información sobre cuantificadores.
Mena
2
Puede usar grupos de captura con nombre en Java 5/6 con named-regexp.
16

Esto está totalmente bien.

  1. El primer grupo ( m.group(0)) siempre captura el área completa que está cubierta por su expresión regular . En este caso, es toda la cadena.
  2. Las expresiones regulares son codiciosas por defecto, lo que significa que el primer grupo captura la mayor cantidad posible sin violar la expresión regular. La (.*)(\\d+)(la primera parte de tu expresión regular) cubre el ...QT300int del primer grupo y el 0del segundo.
  3. Puede solucionar esto rápidamente haciendo que el primer grupo no sea codicioso: cambie (.*)a (.*?).

Para obtener más información sobre codicioso vs perezoso, consulte este sitio.

f1sh
fuente
4

Desde el documento:

Capturing groups</a> are indexed from left
 * to right, starting at one.  Group zero denotes the entire pattern, so
 * the expression m.group(0) is equivalent to m.group().

Entonces, el grupo de captura 0 envía toda la línea.

Michael Laffargue
fuente
3

Su comprensión es correcta. Sin embargo, si caminamos por:

  • (.*) tragará toda la cuerda;
  • necesitará devolver caracteres para que (\\d+)se satistifique (por eso 0se captura, y no 3000);
  • el último (.*)capturará el resto.

Sin embargo, no estoy seguro de cuál era la intención original del autor.

fge
fuente