No artigo anterior demonstrei a utilização dos quantificadores regex para buscar um padrão simples de uma tag HTML. Continuarei com o exemplo demonstrando a utilização das expressões regulares na plataforma Java.
Para começar crie uma classe chamada Conexao no pacote ex.regex. Esta classe encapsula um BufferedReader para leitura do stream de conexão com uma URL.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package ex.regex; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; class Conexao { private BufferedReader reader; Conexao(URL url) throws IOException { reader = new BufferedReader(new InputStreamReader(url.openStream())); } String ler() throws IOException { return reader.readLine(); } void fechar() throws IOException { reader.close(); } @Override protected void finalize() throws Throwable { reader.close(); } } |
Esta classe recebe no construtor um objeto URL e injeta o InputStreamReader criado pelo método openStream() deste objeto no BufferedReader para posterior leitura. Os métodos ler() e fechar() são bem sugestivos.
Agora é hora de criar a classe ExemploRegex no pacote ex.regex. Incialmente apenas com três membros privados. O primeiro será uma String constante que reprensenta nosso regex. O segundo um objeto Pattern e o terceiro um objeto Matcher. As classes Pattern e Matcher fazem parte do pacote java.util.regex.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package ex.regex; import java.io.IOException; import java.io.Console; import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; import ex.regex.Conexao; public class ExemploRegex { private final String REGEX = "<img src=['\"]([^'\"]+)"; private final Pattern pattern = Pattern.compile(REGEX); private final Matcher matcher = pattern.matcher(""); } |
“Em resumo um objeto Pattern é uma expressão regular compilada que pode ser aplicada a qualquer numero de strings, o objeto Matcher é uma instância individual desta expressão regular sendo aplicada a uma string específica.”(Mastering Regular Expressions, FRIEDL, JEFFREY E. F.)
Através do método matcher(), criamos um novo objeto Matcher. Este método é interessante, pois recebe um CharSequence (String, StringBuffer, etc) como parâmetro. O objeto matcher trabalha como o motor de buscas que confronta a sequência de caracteres com nosso Pattern. A classe Matcher oferece uma rica API para buscar e retornar resultados de buscas com regex.
Até agora tudo o que temos é nossa regex de captura de URL de imagens e uma sequência de caracteres vazia. Implementaremos na classe ExemploRegex o método privado processarURL().
1 2 3 4 5 6 7 8 9 10 11 12 | private void processarURL(String url) throws IOException { String s; Conexao cn = new Conexao(new URL(url)); while ((s = cn.ler()) != null) { matcher.reset(s); while (matcher.find()) { System.out.println(matcher.group(1)); } } cn.fechar(); } |
O método processarURL() cria um objeto Conexao para a URL desejada e percorre linha a linha o Reader.
A chamada do método reset() da classe Matcher descarta todas as informações de estado do nosso Matcher informando um novo CharSequence como parâmetro para reiniciarmos nossa pesquisa. O novo CharSequence é a próxima linha obtida do stream.
O método find() tenta encontrar na sequência um padrão equivalente ao Pattern usado para construir este Matcher. Quando encontrado, o primeiro grupo da expressão é impresso, através da chamada a group().
Capturando Grupos
Em regex, os grupos de captura são definidos por parênteses e numerados sequencialmente da esquerda para direita, sendo que o grupo zero representa o Pattern inteiro.
Grupos podem causar um pouco de sobrecarga nas expressões regulares mais complexas pois criam uma backreference para a expressão dentro do grupo, o que pode prejudicar o desempenho das buscas. Sempre sinalize os grupos que não deseja capturar da seguinte maneira com (?:).
Por exemplo: num Matcher criado por um Pattern com expressão regular “(?:Guarda)-(roupas|chuva)”, a chamada do método group(1) retornaria a palavra roupas ou chuva. Grupos marcados com (?:) são ignorados (não são armazenados e não criam backreferences).
Voltando a classe ExemploRegex, agora precisamos de um método main que nos permita entrar com uma URL para ser processada.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static void main(String[] args) throws IOException { Console console = System.console(); if (console == null) { System.err.println("O console não pôde ser encontrado."); System.exit(1); } ExemploRegex exr = new ExemploRegex(); String url = ""; while (!url.equalsIgnoreCase("sair")) { url = console.readLine("Entre com a URL do documento (CTRL+C ou 'sair' para terminar): "); exr.processarURL(url); } } |
Verificamos o console e em seguida iniciamos uma iteração que aguarda a entrada da URL do documento para processar. Execute a classe e experimente entrar com a URL de um site como http://www.gamespot.com/pc e veja o resultado.
Considerações Finais
A utilização de expressões regulares combinando as classes Pattern e Matcher do pacote java.util.regex abre um novo leque para o desenvolvedor que precisa encontrar padrões numa sequência de texto com formato não padronizado.
Com elas pode-se encontrar padrões específicos de maneira elegante e simples. Veja quantos operadores condicionais foram necessários para encontrar o que queríamos.
O exemplo apresentado é bem simples e você pode expandí-lo para uso próprio.
Links recomendados:
http://aurelio.net/er/
http://www.regular-expressions.info/brackets.html
http://java.sun.com/javase/6/docs/api/java/util/regex/package-summary.html
http://java.sun.com/docs/books/tutorial/essential/regex/index.html
Livros Recomendados
Mastering Regular Expressions
FRIEDL, JEFFREY E. F.
OREILLY & ASSOC
Real World Regular Expressions With Java 1.4
HABIBI, MEHRAN
APRESS