Expressões Regulares (java.util.regex.Matcher e java.util.regex.Pattern)
Buenas pessoal, nesse post estarei falando sobre Expressão Regular (ou regex, do inglês Regular Expression).
Aviso: “Este post não vai te transformar de iniciante em regex a um guru de regex” (frase adaptada do livro Certificação Sun para Programador Java 5, Guia de Estudo – Kathy Sierra e Bert Bates).
O regex é uma maneira de procurar um determinado padrão em um determinado texto. (para melhor entendimento do conceito ou conhecer a história do regex: http://pt.wikipedia.org/wiki/Expressão_regular)
Exemplo de regex teórico:
Texto: Olá Vânio, como você é legal!
Padrão: letras a,b,c,d,e.
A busca seria feita e o retorno seria:
a na posição 27
c nas posições 12,19
e na posição 25.
Note que não foram retornadas as letras acentuadas. Isso aconteceu porque a busca realmente não foi feita por letras com acentuação.
Em Java…
Por default também são diferenciadas letras maiúsculas de minúsculas, mas isso pode ser alterado quando o padrão for compilado (mostrarei exemplo).
Nesse exemplo teórico a posição (índice) é mostrada assumindo que o primeiro caractere da palavra começa por 1, porem em Java a contagem começa por 0 (zero), a partir de agora esse post tratará os índices como no Java. Em Java a posição do “a” encontrado seria a 26.
Como está organizado o regex em Java
As classes responsáveis pelo regex em Java estão no pacote java.util.regex. As classes que possivelmente você vai se deparar são:
Pattern;
Matcher;
PatternSyntaxException.
Pattern: é a classe que vai representar o padrão a ser procurado. Ela não possui construtor, você vai precisar usar o método public static Pattern compile(String s) ou o seu método sobrecarregado public static Pattern compile(String s, int flag) para obter uma instancia de Pattern.
Matcher: é a classe que vai gerenciar toda a pesquisa (faz as buscas, retorna resultados e etc). Ela também não possui construtor e você terá que usar o método public Matcher matcher(String s) da classe Pattern para obter a intancia de Matcher.
PatternSysntaxException: é a exception lançada ao compilar um pattern inválido.
Buscas Simples
Exemplos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package br.com.vaniomeurer.blog.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Exemplo1 { public static void main(String[] args) { // O padrão será "es" Pattern pattern = Pattern.compile("es"); // O texto será "busca simples" Matcher matcher = pattern.matcher("busca simples"); while (matcher.find()) { System.out.println(matcher.start() + " - " + matcher.group()); } } } |
A saída será:
11 – es
O método find() da classe Matcher, verifica se há ocorrência do padrão no texto, se há, ele assume que tal parte do texto foi utilizada, e no próximo find() que for executado, ele começará a procura a partir do primeiro índice depois do ultimo índice do padrão encontrado no texto (exceto quando usa-se quantificadores relutantes, mas já sai do escopo desse post).
O método start() da classe Matcher retorna o índice inicial do padrão encontrado no texto. Existe também o método end(), que ao contrario do start(), retorna o índice final.
O método group() da classe Matcher retorna a String encontrada com o padrão.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package br.com.vaniomeurer.blog.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Exemplo2 { public static void main(String[] args) { Pattern pattern = Pattern.compile("ese"); Matcher matcher = pattern.matcher("ese esese das"); while (matcher.find()) { System.out.println(matcher.start() + " - " + matcher.group()); } } } |
A saída será:
0 – ese
4 – ese

O ponto vermelho indica onde o Matcher vai começar a pesquisa depois das execuções do find()
Esses são exemplos de Buscas Simples, onde o padrão é uma String pura, mas e se nós precisássemos buscar qualquer letra?
Busca com metacaracteres
Os metacaracteres são caracteres especiais que são usados no padrão para que nossa busca se torne mais poderosa! (UAU!)
A seguir uma lista com alguns metacaracteres importantes:
- \d – representa números.
- \s – representa um espaço em branco.
- \w – representa letras, números ou o “_” (underscore).
- . – representa qualquer digito.
- [] – representa uma cadeia de valores. Ex: [a-c] buscaria a ou b ou c.
- ? – representa zero ou uma ocorrência.
- * – representa zero ou mais ocorrências.
- + – representa uma ou mais ocorrências.
- ^ – representa negação.
- () – agrupa os padrões, usado com os metacaracteres acima. Ex: (.)* retornaria o texto inteiro.
Para poder usar os metacaracteres \d, \s, \w você precisa adicionar uma barra invertida (\) na frente do metacaracter caso contrario o compilador vai interpretar isso como um caractere de escape.
Exemplo de metacaracter com escape: \\d
Com os metacaracteres nossos padrões começam a ficar interessantes, digamos que você precisa encontrar os telefones em uma lista onde estão disponíveis telefones no formato 0000-0000, como seria nosso padrão?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package br.com.vaniomeurer.blog.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class BuscarTelefone { public static void main(String[] args) { // Nosso padrão. String padrao = "\\d\\d\\d\\d-\\d\\d\\d\\d"; // Nosso texto simulando uma lista cheia de telefones. (o \n é para // simular várias linhas) String lista = "12341234\n1324-1234\n9900-0000\n1234567\nXC484714"; Pattern pattern = Pattern.compile(padrao); Matcher matcher = pattern.matcher(lista); while (matcher.find()) { System.out.println(matcher.start() + " - " + matcher.group()); } } } |
A saída seria:
9 – 1324-1234
19 – 9900-0000
É um padrão básico que vai procurar quatro números (\d), um “-” e mais quatro números.
Interessante, não? Ainda não! Vamos pensar que nós temos também na lista telefones no formato 00000000, ou seja, queremos os telefones nos formatos: 00000000 e 0000-0000, o que precisamos alterar no código? Vamos usar o metacaracter ? (zero ou um).
Alterando o código para:
11 | String padrao = "\\d\\d\\d\\d(-)?\\d\\d\\d\\d"; |
A saída agora fica:
0 – 12341234
9 – 1324-1234
19 – 9900-0000
A grande sacada foi o uso do quantificador “?”.
Legal, mas foi usado ( e ) mas isso não foi incluso no padrão, caso eu quisesse procurar por (, como faria?
Precisariamos escapar o que fossemos procurar, exemplos:
Caracter “(“, ficaria: “\\(” (usamos duas barras pois uma barra o Java entende como caractere de escape comum (\n,\t e etc) e com a outra barra estamos escapando a barra para que ela seja usada).
Caracter “.”, ficaria “\\.”.
Alguns outros exemplos:
O “[]“
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package br.com.vaniomeurer.blog.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Exemplo3 { public static void main(String[] args) { // Esse padrão busca de a até z e A até Z. Matcher matcher = Pattern.compile("[a-zA-Z]").matcher( "A b C d x 5 7 1 - x _"); while (matcher.find()) { System.out.println(matcher.start() + " - " + matcher.group()); } } } |
A saída:
0 – A
2 – b
4 – C
6 – d
8 – x
18 – x
O “+”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package br.com.vaniomeurer.blog.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Exemplo4 { public static void main(String[] args) { // Se não fosse usado o metacaracter + o padrão não buscaria números com mais de 2 dígitos. Matcher matcher = Pattern.compile("\\d+").matcher( "A b C d x 5 7 1 - x _ 156 1 5 48"); while (matcher.find()) { System.out.println(matcher.start() + " - " + matcher.group()); } } } |
A saída:
10 – 5
12 – 7
14 – 1
22 – 156
26 – 1
28 – 5
30 – 48
O “^”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package br.com.vaniomeurer.blog.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Exemplo5 { public static void main(String[] args) { // Note o uso do && para juntar dois conjuntos // Esse padrão busca de a até z, menos o x. Matcher matcher = Pattern.compile("[a-z&&[^x]]").matcher( "A b C d x 5 7 1 - x _"); while (matcher.find()) { System.out.println(matcher.start() + " - " + matcher.group()); } } } |
A saída:
2 – b
6 – d
Como prometi no começo do post, um exemplo de como retirar o Case Sensitive (diferenciar letra maiúscula de minúscula). Note que esse exemplo fica igual o exemplo 3.
1 2 3 4 5 6 7 8 9 10 11 12 13 | package br.com.vaniomeurer.blog.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Exemplo6 { public static void main(String[] args) { // Esse padrão busca de a até z e A até Z. Matcher matcher = Pattern.compile("[a-z]", Pattern.CASE_INSENSITIVE).matcher( "A b C d x 5 7 1 - x _"); while (matcher.find()) { System.out.println(matcher.start() + " - " + matcher.group()); } } } |
A saída:
0 – A
2 – b
4 – C
6 – d
8 – x
18 – x
Por hoje é isso, espero que esse post possa ajudar alguém no mundo das regex.
Vânio Meurer (vaninhO).