Grupos de captura Java Regex

170

Estou tentando entender esse bloco de código. No primeiro, o que procuramos na expressão?

Meu entendimento é que é qualquer caractere (0 ou mais vezes *) seguido por qualquer número entre 0 e 9 (uma ou mais vezes +) seguido por qualquer caractere (0 ou mais vezes *).

Quando isso é executado, o resultado é:

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

Alguém poderia passar por isso comigo?

Qual é a vantagem 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
fonte
1
Para inserir uma nova linha, coloque 2 espaços no final da linha. Mais sobre sintaxe de remarcação: en.wikipedia.org/wiki/Markdown - Veja também: stackoverflow.com/editing-help
assylias

Respostas:

248

O problema que você está enfrentando é com o tipo de quantificador. Você está usando um quantificador ganancioso no seu primeiro grupo (o índice 1 - o índice 0 representa o todo Pattern), o que significa que ele corresponderá o máximo possível (e, como é um caractere, ele corresponderá a tantos caracteres quantos houver) para cumprir a condição para os próximos grupos).

Em resumo, seu 1º grupo .*corresponde a qualquer coisa, desde que o próximo grupo \\d+possa corresponder a alguma coisa (nesse caso, o último dígito).

De acordo com o terceiro grupo, ele corresponderá a qualquer coisa após o último dígito.

Se você o alterar para um quantificador relutante em seu primeiro grupo, obterá o resultado que suponho que esteja esperando, ou seja, a parte 3000 .

Observe o ponto de interrogação no 1º 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));
}

Resultado:

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

Mais informações sobre Java Pattern aqui .

Finalmente, os grupos de captura são delimitados por colchetes e fornecem uma maneira muito útil de usar referências posteriores (entre outras coisas), assim que você Patterncorresponder à entrada.

No Java 6, os grupos só podem ser referenciados por sua ordem (cuidado com os grupos aninhados e a sutileza da ordem).

No Java 7, é muito mais fácil, pois você pode usar grupos nomeados.

Mena
fonte
Obrigado! É o motivo pelo qual o grupo 2 armazenou 0 porque toda a linha foi consumida pelo quantificador ganancioso, que recuou até entrar em contato com um ou mais números. 0 satisfez isso e a expressão teve êxito. Acho o terceiro grupo confuso, esse quantificador ganancioso também consome toda a linha, mas recua até encontrar o um ou mais números (\\ d +) que deveria precedê-lo?
Xivilai
@Xivilai, deixe-me afinar minha explicação na minha resposta, apenas um segundo.
Mena
Essa é uma boa explicação. Assim, o relutante começa a partir da esquerda e apenas leva o mínimo, enquanto que com os gananciosos, será preciso o máximo possível (começando pela direita), parando apenas antes do último dígito para satisfazer essa condição. O terceiro grupo leva o resto.
Xilai
@Xivilai mais ou menos. Sempre começa da esquerda, neste caso. Aqui estão mais algumas informações sobre quantificadores.
Mena
2
Você pode usar grupos de captura nomeados no Java 5/6 com named-regexp.
16

Isso é totalmente bom.

  1. O primeiro grupo ( m.group(0)) sempre captura toda a área coberta por sua expressão regular . Nesse caso, é toda a cadeia.
  2. Expressões regulares são gananciosas por padrão, o que significa que o primeiro grupo captura o máximo possível sem violar a regex. O (.*)(\\d+)(a primeira parte do seu regex) abrange o ...QT300int do primeiro grupo e o 0do segundo.
  3. Você pode corrigir isso rapidamente, tornando o primeiro grupo não ganancioso: mude (.*)para (.*?).

Para mais informações sobre ganancioso x preguiçoso, consulte este site.

f1sh
fonte
4

Do 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().

Portanto, o grupo de captura 0 envia a linha inteira.

Michael Laffargue
fonte
3

Sua compreensão está correta. No entanto, se analisarmos:

  • (.*) engolirá toda a corda;
  • ele precisará devolver os personagens para que (\\d+)sejam saturados (e é por isso que 0é capturado, e não 3000);
  • o último (.*)capturará o restante.

Não sei ao certo qual era a intenção original do autor.

fge
fonte