Arquivo

Textos com Etiquetas ‘java.util.regex’

Expressões Regulares (java.util.regex.Matcher e java.util.regex.Pattern)

24, março, 2009

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

Representação de onde o Matcher vai começar a procura após o find()

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

Vanio Meurer Java SE , , , ,