Utilizando Expressões Regulares em Java com Pattern e Matcher

Este artigo foca na aplicação de expressões regulares em Java, especificamente como utilizar as classes Pattern e Matcher do pacote java.util.regex para realizar correspondências de padrões em strings.

Primeiros Passos com Correspondência de Padrões

A abordagem geral envolve definir uma string de padrão (regex) e uma string de texto a ser verificada. O objetivo é determinar se o texto contém uma sequência de caracteres que satisfaz o padrão.


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

public class ExemploBasico {
   public static void main(String[] args) {
       // O padrão "[a-z]{3}" busca por exatamente 3 caracteres minúsculos de 'a' a 'z'.
       Pattern padrao = Pattern.compile("[a-z]{3}");
       Matcher comparador = padrao.matcher("abc");
       System.out.println(comparador.matches()); // Saída: true
   }
}
   
  • Pattern pode ser visto como um modelo compilado de uma expressão regular. A compilação prévia otimiza o desempenho, especialmente para padrões usados repetidamente, e o objeto Pattern compilado é seguro para uso concorrente por múltiplos threads.
  • Matcher representa o resultado da comparação de uma string com um padrão. Uma string pode gerar múltiplos resultados de correspondência, como veremos em exemplos posteriores.
  • O método matcher.matches() verifica se a *string inteira* corresponde ao padrão.
  • Uma forma mais concisa para uma única verificação é "abc".matches("[a-z]{3}"). No entanto, essa abordagem é menos eficiente se o mesmo padrão precisar ser aplicado várias vezes.

Correspondência Completa vs. Parcial

O exemplo anterior demonstrou uma correspondência básica. Vamos explorar a diferença entre correspodnência completa e parcial.


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

public class ExemploCompletoParcial {
   public static void main(String[] args) {
       Pattern padrao = Pattern.compile("[a-z]{3}"); // Padrão: 3 caracteres minúsculos
       Matcher comparador = padrao.matcher("abcd");  // Texto: "abcd"
       System.out.println(comparador.matches()); // Saída: false
   }
}
   

O resultado é false porque, embora "abc" corresponda ao padrão, a string inteira "abcd" não corresponde. Isso ocorre porque matches() exige uma correspondência completa.

  • Correspondência Completa: A string inteira deve corresponder ao padrão.
  • Correspondência Parcial: Uma sub-string dentro do texto corresponde ao padrão.

Para verificar se *qualquer parte* do texto corresponde ao padrão (correspondência parcial), utilize o método find():


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

public class ExemploFind {
   public static void main(String[] args) {
       Pattern padrao = Pattern.compile("[a-z]{3}"); // Padrão: 3 caracteres minúsculos
       Matcher comparador = padrao.matcher("abcd");  // Texto: "abcd"
       System.out.println(comparador.find()); // Saída: true (porque "abc" corresponde)
   }
}
   

Ancoragem de Início e Fim (^ e $ )

Os delimitadores de início (^) e fim ($) podem ser usados para forçar uma correspondência completa, mesmo ao usar find().


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

public class ExemploAncoragem {
   public static void main(String[] args) {
       // Padrão com âncoras: string inteira deve ter exatamente 3 caracteres minúsculos
       Pattern padrao = Pattern.compile("^[a-z]{3}$");
       Matcher comparador = padrao.matcher("abcd");
       System.out.println(comparador.find()); // Saída: false
   }
}
   

Com o padrão ^[a-z]{3}$, find() retorna false para "abcd" porque as âncoras exigem que o padrão cubra a string do início ao fim.

Se o texto corresponder exatamente:


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

public class ExemploAncoragemSucesso {
   public static void main(String[] args) {
       Pattern padrao = Pattern.compile("^[a-z]{3}$");
       Matcher comparador = padrao.matcher("abc"); // Texto: "abc"
       System.out.println(comparador.find()); // Saída: true
   }
}
   

Correspondência com Grupos

Expressões regulares podem capturar partes específicas do texto correspondente usando parênteses (()). Estes são chamados de grupos de captura.

Considere o seguinte trecho de configuração de firewall:


access-list 1 line 3 extended permit object TCP-33333 object WS-172.21.2.10_32 object WS-11.1.1.10_32 (hitcnt=0) 0xb206976e
   

Suponha que queremos extrair o valor HASH (0xb206976e) e partes específicas da linha.


import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils; // Assumindo esta lib para isBlank

public class ExemploGrupo {
   public static void main(String[] args) {
       String textoBruto = "access-list 1 line 3 extended permit object TCP-33333 object WS-172.21.2.10_32 object WS-11.1.1.10_32 (hitcnt=0) 0xb206976e";
       String partePolitica = "access-list 1 line 3 extended permit object TCP-33333 object WS-172.21.2.10_32 object WS-11.1.1.10_32";

       // Escapa caracteres especiais no padrão específico da política
       String politicaEscapada = partePolitica.replaceAll("\\.", "\\\\.");

       // Regex para encontrar a linha e capturar grupos específicos
       // (Grupo 1: O início da linha até o policy)
       // (Grupo 2: Espaços após o policy)
       // (Grupo 3: Conteúdo opcional entre parênteses, como (hitcnt=0)) - pode aparecer 0 ou mais vezes
       // (Grupo 4: O valor HASH no formato 0x...)
       String regex = "(" + politicaEscapada + ")(\\s*)(.*?)(\\s)(0x\\S+)";

       Pattern ciscoPattern = Pattern.compile(regex);
       Matcher comparador = ciscoPattern.matcher(textoBruto);

       if (comparador.find()) {
           System.out.println("Grupo 0 (Todo o Match): " + comparador.group(0));
           System.out.println("Grupo 1 (Política): " + comparador.group(1));
           System.out.println("Grupo 2 (Espaços): '" + comparador.group(2) + "'");
           System.out.println("Grupo 3 (Info Adicional): " + comparador.group(3)); // Pode conter hitcnt, inactive, etc.
           System.out.println("Grupo 4 (HASH): " + comparador.group(4));
       } else {
           System.out.println("Não houve correspondência.");
       }
   }
}
   

No código acima:

  • comparador.group(0) retorna toda a string que correspondeu ao padrão.
  • comparador.group(1) retorna o conteúdo do primeiro parêntese na regex (a política específica).
  • comparador.group(2) retorna os espaços entre a política e o conteúdo adicional.
  • comparador.group(3) captura informações como (hitcnt=0) ou (inactive). A parte .* é gulosa, mas o ? a torna preguiçosa, parando na primeira ocorrência possível antes do próximo elemento.
  • comparador.group(4) captura o valor HASH (0xb206976e).

Note que a regex foi ajustada para ser mais robusta na captura de informações adicionais entre a política e o HASH.

Tags: java Regex Pattern Matcher Expressões Regulares

Publicado em 10-28 00:32