• No se han encontrado resultados

Compare y extraiga subcadenas empleando la API de expresiones regulares

In document Java soluciones de programación (página 53-56)

Componentes clave

Clases Métodos

java.util.regex.Pattern Pattern.compile(String expReg) Matcher matcher(CharSequence cad) java.util.regex.Matcher boolean find( )

String group( )

Aunque el soporte de String a expresiones regulares es muy útil, no proporciona acceso a todas las características soportadas por la API de expresiones regulares. Hay ocasiones en que necesitará usar directamente la API de expresiones regulares. En tales ocasiones querrá obtener una subcadena que busque coincidencias de un patrón general. Por ejemplo, suponga que tiene una cadena que contiene una lista de la información para ponerse en contacto con los empleados de una empresa, que incluye números telefónicos y direcciones de correo electrónico. Más aún, suponga que quiere extraer las direcciones de correo electrónico de esta cadena. ¿Cómo realiza esto? Aunque la API de Java ofrece varias maneras de resolver este problema, la más sencilla consiste en usar las clases Pattern y Matcher defi nidas por la API de expresiones regulares. En esta solución se muestra cómo completar esta tarea.

Paso a paso

Para obtener una subcadena que busca coincidencias de una expresión regular se requieren los siguientes pasos:

1. Cree una instancia de Pattern al llamar a su método de fábrica compile( ). Pase a compile( ) la expresión regular que describe el patrón que está buscando.

2. Cree un Matcher que contiene la cadena de entrada al llamar a matcher( ) en el objeto Pattern.

3. Llame a fi nd( ) en Matcher para buscar una coincidencia. Devuelve verdadero si se encuentra la secuencia, y falso si no.

4. Si fi nd( ) tiene éxito, llame a group( ) en Matcher para obtener la secuencia coincidente.

Análisis

La clase Pattern no defi ne constructores. En cambio, se crea un patrón al llamar el método de fábrica compile( ). Aquí se muestra la forma usada en esta solución:

static Pattern compile(String expReg)

Aquí, expReg es la expresión regular que quiere encontrar. El método compile( ) transforma expReg en un patrón que la clase Matcher puede usar para buscar coincidencias de patrones. Devuelve un objeto Pattern que contiene el patrón. Si expReg especifi ca una expresión no válida, se lanza una PatternSyntaxException.

Una vez que haya creado un objeto de Pattern, lo usará para crear uno de Matcher. Matcher no tiene constructores. En cambio, se crea un objeto de Matcher al llamar al método de fábrica matcher( ) defi nido por Pattern. Aquí se muestra:

Matcher matcher(CharSequence cad)

Aquí, cad es la secuencia de caracteres contra la que se comparará el patrón. CharSequence es una interfaz que defi ne un conjunto de caracteres de sólo lectura. Está implementado por la clase String, entre otras. Por tanto, puede pasar una cadena a matcher( ).

Para determinar si una subsecuencia de la secuencia de entrada coincide con el patrón, se llama a fi nd( ) en Matcher. Tiene dos versiones; la que usamos aquí es:

boolean find( )

Devuelve verdadero si hay una secuencia coincidente y falso, de otra manera. A este método se le puede llamar varias veces, lo que le permite encontrar todas las secuencias coincidentes. Cada llamada a fi nd( ) empieza donde se deja la anterior.

Para obtener la cadena que contiene la coincidencia actual, se llama a group( ). La forma usada aquí es:

String group( )

Se devuelve la cadena coincidente. Si no existe una coincidencia, entonces se lanza una IllegalStateException.

Ejemplo

Con el siguiente programa se muestra un ejemplo que extrae direcciones de correo electrónico de la forma nombre@XYZ de una cadena que contiene información para ponerse en contacto con los empleados de una empresa imaginaria llamada XYZ.

// Extrae una subcadena al buscar coincidencias de una expresión regular. import java.util.regex.*;

class UsaExpReg {

// Crea una instancia de Pattern cuya expresión regular // coincide con direcciones de correo electrónico de // cualquier empleado de XYZ.com.

Pattern pat = Pattern.compile("\\b\\w+@XYZ\\.com\\b"); // Crea un Matcher para el patrón.

Matcher mat = pat.matcher("Informaci\u00a2n de contacto de la empresa\n" + "Juan 555–1111 juan@XYZ.com\n" +

"Martha 555–2222 Martha@XYZ.com\n" + "Daniel 555–3333 Daniel@XYZ.com");

// Encuentra y despliega todas las direcciones de correo electrónico. while(mat.find( ))

System.out.println("Coincidencia: " + mat.group( )); }

}

A continuación se muestra la salida: Coincidencia: juan@XYZ.com

Coincidencia: Martha@XYZ.com Coincidencia: Daniel@XYZ.com

Opciones

Puede habilitar coincidencias insensibles a diferencias entre mayúsculas y minúsculas empleando esta forma de compile( ).

static Pattern compile(String expReg, int opciones)

Aquí, expReg es la expresión regular que describe el patrón y opciones contiene uno o más de los siguientes valores (defi nidos por Pattern):

CASE_INSENSITIVE CANON_EQ COMMENTS

DOTALL LITERAL MULTILINE

UNICODE_CASE UNIX_LINES

Excepto por CANON_EQ, estas opciones tienen el mismo efecto que las marcas correspondientes de expresiones regulares, como (?i), descritas antes en este capítulo. (No hay una marca

correspondiente de expresión regular para CANON_EQ.) Para coincidencias insensibles a diferencias entre mayúsculas y minúsculas, pase CASE_INSENSITIVE a opciones.

Puede obtener el índice de la coincidencia correspondiente dentro de la cadena de entrada al llamar a start( ). El índice uno pasado al fi nal de la coincidencia actual se obtiene al llamar a end( ). Aquí se muestran estos métodos:

int start( ) int end( )

Ambos lanzan una IllegalStateException si no se ha tenido ninguna coincidencia. Esta información es útil si quiere eliminar la coincidencia de la cadena, por ejemplo.

Dividir en fi chas una cadena es una tarea de programación que casi cualquier programador enfrentará en un momento u otro. Dividir en fi chas es el proceso de reducir una cadena a sus partes individuales, que se denominan fi chas. Por tanto una fi cha representa el elemento indivisible más pequeño que puede extraerse de una cadena y que tiene algún signifi cado.

Por supuesto, lo que constituye una fi cha depende del tipo de entrada que se está procesando, y del propósito. Por ejemplo, cuando se divide en fi chas una cadena que contiene texto, las partes individuales son palabras, signos de puntuación y números. Cuando se dividen los elementos de un programa, las partes incluyen palabras clave, identifi cadores, operadores, separadores, etc. Cuando se divide un fl ujo de datos que contiene información de bolsa de valores, las fi chas podrían ser el nombre de la empresa, su precio actual y se relación P/E. El punto clave es que habrá de cambiar lo que constituye una fi cha, dependiendo de la circunstancia.

Es posible dividir una cadena en fi chas con dos métodos básicos. El primero se basa en defi nir los delimitadores que separan una fi cha de la otra. Este método suele ser útil cuando se divide en fi chas un fl ujo de datos que usa un formato fi jo. Por ejemplo, los datos accionarios en tiempo real podrían estar disponibles en la siguiente forma:

nombre, precio, relación P/E | nombre, precio, relación P/E | ...

Aquí, los datos están en un formato fi jo en que cada fragmento de información de la compañía está separado del siguiente por una barra vertical, y los valores asociados con cada empresa están separados de los demás por comas. En este caso, pude usar el método aplit( ) defi nido por String para reducir esa cadena en sus fi chas individuales al especifi car ",|" como el conjunto de delimitadores.

Sin embargo, no siempre es apropiado o conveniente un método basado en un delimitador. En algunos casos lo que constituye un delimitador variará de un caso a otro dentro de la misma cadena. Los programas de cómputo son un ejemplo importante de esto. Por ejemplo, dado este fragmento

hecho = long <= (12/puerto23);

¿Cuál conjunto de delimitadores dividirá esta cadena en sus fi chas individuales? Evidentemente, no puede usar espacios en blanco, porque 12 no está separado de /, aunque ambos constituyen fi chas individuales. Además, el identifi cador puerto23 contiene letras y dígitos, de modo que no es válido especifi car dígitos como delimitadores. Más aún, los operadores =, <= y / deben devolverse como fi chas, lo que signifi ca que no pueden usarse como delimitadores. En general, lo que separa a una fi cha de otra se basa en la sintaxis y la semántica del programa, no en un formato fi jo. Cuando se enfrenta este tipo de situación de división en fi chas, debe usarse el segundo método.

En lugar de dividir en fi chas con base en delimitadores, el segundo método extrae cada fi cha con base en el patrón con el que coincide. Por ejemplo, cuando se divide un programa, un identifi cador típico buscará coincidencias de un patrón que empieza con una letra o un carácter de

In document Java soluciones de programación (página 53-56)