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
}
}
Patternpode 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 objetoPatterncompilado é seguro para uso concorrente por múltiplos threads.Matcherrepresenta 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.