Expresiones Regulares en Java

Las expresiones regulares son algo que se usa desde hace años en otros lenguajes de programación como Perl, Sed o Awk. En la versión 1.4 del JDK de Sun se incluye el paquete java.util.regex, que proporciona una serie de clases para poder hacer uso de la potencia de este tipo de expresiones en Java. Antes de nada necesitamos saber qué es una expresión regular y para que nos puede servir:

Pues bien, una expresión regular es un patrón que describe a una cadena de caracteres. Todos hemos utilizado alguna vez la expresión *.doc para buscar todos los documentos en algún lugar de nuestro disco duro, pues bien, *.doc es un ejemplo de una expresión regular que representa a todos los archivos con extensión doc, el asterisco significa cualquier secuencia de caracteres (vale, los que ya conozcan esto dirán que no es correcto, y dirán bien, es mas preciso hablar de *.doc pero el ejemplo es muy gráfico).

Las expresiones regulares se rigen por una serie de normas y hay una construcción para cualquier patrón de caracteres. Una expresión regular sólo puede contener (aparte de letras y números) los siguientes caracteres:


<    $, ^, ., *, +, ?, [, ], .    >

Una expresión regular, nos servirá para buscar patrones en una cadena de texto, por ejemplo encontrar cuantas veces se repite una palabra en un texto, para comprobar que una cadena tiene una detereminada estructura, por ejemplo que el nombre de archivo que nos proponen tiene una determinada extensión, o comprobar que un email esta bien escrito... Para cada uno de estos casos existe una expresión regular que los representa:

  • Por medio de la expresión regular "camion" podemos encontrar cuantas veces se repite camión en un texto. Es la construcción mas sencilla.
  • Esta expresión "^www.*.es" comprueba que una cadena sea una dirección web que comience por www y sea de un servidor español.
  • Y esta, para ver la potencia de las expresiones regulares, comprueba la buena formación de los correos electrónicos: "[^A-Za-z0-9.@_-~#]+".
    [Para ver como formar expresiones regulares puede ir al Apéndice A]

Uso

El paquete java.util.regex esta formado por dos clases, la clase Matcher y la clase Pattern y por una excepción, PatternSyntaxException.

La clase Pattern (segun la documentacion del jdk1.4) es la representacion compilada de una expresion regular, o lo que es lo mismo, representa a la expresion regular, que en el paquete java.util.regex necesita estar compilada. En castellano significa patrón.

La clase Matcher es un tipo de objeto que se crea a partir de un patrón mediante la invocación del método Pattern.matcher. Este objeto es el que nos permite realizar operaciones sobre la secuencia de caracteres que queremos validar o la en la secuencia de caracteres en la que queremos buscar. En castellano lo mas parecido a esto es la palabra encajador.

Por lo tanto tenemos patrones que deben ser compilados, a partir de estos creamos objetos Matcher (encajadores) para poder realizar las operaciones sobre la cadena en cuestión.

Vamos con la clase Pattern, para crear un patrón necesitamos compilar una expresión regular, esto lo conseguimos con el método compile:

Pattern patron = Pattern.compile("camion");

El método pattern devuelve la expresión regular que hemos compilado, el método matcher crea un objeto Matcher a partir del patrón, el método split divide una cadena dada en partes que cumplan el patrón compilado y por último el método matches compila una expresión regular y comprueba una cadena de caracteres contra ella.

Ahora la clase Matcher. Esta clase se utiliza para comprobar cadenas contra el patrón indicado. Un objeto Matcher se genera a partir de un objeto Pattern por medio del método matcher:

Pattern patron = Pattern.compile("camion");
Matcher encaja = patron.matcher();

Una vez que tenemos el objeto creado, podemos realizar tres tipos de operaciones sobre una cadena de caracteres. Una es a través del método matches que intenta encajar toda la secuencia en el patrón (para el patrón "camion" la cadena "camion" encajaría, la cadena "mi camion es verde" no encajaría). Otra es a través del método lookingAt, intenta encajar el patrón en la cadena (para el patrón "camion" tanto la cadena "camion" como la cadena "mi camion es verde" encajaria). Otra es la proporcionada por el método find que va buscando subcadenas dentro de la cadena de caracteres que cumplan el patrón compilado (una vez encontrada una ocurrencia, se puede inspeccionar por medio de los métodos start que marca el primer carácter de la ocurrencia en la secuencia y el método end que marca el ultimo carácter de la ocurrencia). Todos estos métodos devuelven un booleano que indica si la operación ha tenido éxito.

Todo lo anterior esta orientado a la b6uacutesqueda de patrones en cadenas de caracteres, pero puede que queramos llegar mas allá, que lo que queramos sea reemplazar una cadena de caracteres que se corresponda con un patrón por otra cadena. Por ejemplo un método que consigue esto es replaceAll que reemplaza toda ocurrencia del patrón en la cadena por la cadena que se le suministra.

Ejemplos

El siguiente es un ejemplo del uso del método replaceAll sobre una cadena. El ejemplo sustituye todas las apariciones que concuerden con el patron "a*b" por la cadena "-".

// se importa el paquete java.util.regex
import java.util.regex.*;

public class EjemploReplaceAll{
	public static void main(String args[]){
// compilamos el patron
Pattern patron = Pattern.compile("a*b");
// creamos el Matcher a partir del patron, la cadena como parametro
Matcher encaja = patron.matcher("aabmanoloaabmanoloabmanolob");
// invocamos el metodo replaceAll
String resultado = encaja.replaceAll("-");
System.out.println(resultado);
	}
}

El siguiente ejemplo trata de validar una cadena que supuestamente contiene un email, lo hace con cuatro comprobaciones, con un patrón cada una, la primera que no contenga como primer caracter una @ o un punto, la segunda que no comience por www. , que contenga una y solo una @ y la cuarta que no contenga caracteres ilegales:

import java.util.regex.*;

public class ValidacionEmail {
   public static void main(String[] args) throws Exception {                
      String input = "www.?regular.com";
      // comprueba que no empieze por punto o @
      Pattern p = Pattern.compile("^.|^@");
      Matcher m = p.matcher(input);
      if (m.find())
         System.err.println("Las direcciones email no empiezan por punto o @");
         
      // comprueba que no empieze por www.
      p = Pattern.compile("^www.");
      m = p.matcher(input);
      if (m.find())
        System.out.println("Los emails no empiezan por www");

      // comprueba que contenga @
      p = Pattern.compile("@");
      m = p.matcher(input);
      if (!m.find())
      	System.out.println("La cadena no tiene arroba");
      	
      // comprueba que no contenga caracteres prohibidos	
      p = Pattern.compile("[^A-Za-z0-9.@_-~#]+");
      m = p.matcher(input);
      StringBuffer sb = new StringBuffer();
      boolean resultado = m.find();
      boolean caracteresIlegales = false;

      while(resultado) {
         caracteresIlegales = true;
         m.appendReplacement(sb, "");
         resultado = m.find();
      }

      // Añade el ultimo segmento de la entrada a la cadena
      m.appendTail(sb);

      input = sb.toString();

      if (caracteresIlegales) {
         System.out.println("La cadena contiene caracteres ilegales");
      }
   }
}

Conclusión

Las expresiones regulares vienen a tapar un hueco en el JDK de Sun que venia siendo solicitado desde hace mucho tiempo. Con la inclusión de las expresiones regulares Java se convierte, en este tema, en un lenguaje de programación tan flexible como otros mas tradicionales en el tema de las expresiones regulares, Perl, Awk, etc... Hasta ahora la unica opción para conseguir un efecto parecido era el uso de StringTokenizer en conjunción con llamadas repetidas al método charAt que producía un código demasiado enrevesado. Las expresiones regulares tienen un amplio abanico de posibilidades, principalmente para hacer búsquedas, para sustituir ocurrencias y para comprobar la buena formación de cadenas, como se ha visto en el ejemplo del email.

Enlaces

Java Developers Kit de Sun v.1.4: http://java.sun.com/j2se/

Documentacion de Sun del paquete java.util.regex: http://java.sun.com/j2se/1.4/docs/api/java/util/regex/package-summary.html

Tutorial de expresiones regulares: http://bulmalug.net/body.phtml?nIdNoticia=770

Apéndice A

Lo que viene a continuación no es mas que la traducción del la documentación de una parte de la clase Pattern. Para una referencia completa puede consultar la versión en ingles de Sun inc. sobre la clase Pattern en http://java.sun.com/j2se/1.4/docs/api/java/util/regex/Pattern.html.

Expresion Encaja con
Caracteres

 

x El caracter x
El caracter
n El caracter con valor octal 0n (0 <= n <= 7)
nn El caracter con valor octal 0nn (0 <= n <= 7)
mnn El caracter con valor octal 0mnn (0 <= m <= 3, 0 <= n <= 7)
xhh El caracter con valor hexadecimal 0xhh
uhhhh El caracter con valor hexadecimal 0xhhhh
El tabulador ('u0009')
Nueva linea (line feed) ('u000A')
Retorno de carro ('u000D')
f Nueva pagina ('u000C')
a Un beep de alerta (bell) ('u0007')
e Escape ('u001B')
cx El caracter de control que corresponde a x
Intervalos de caracteres

 

[abc] a, b, o c
[^abc] Cualquier caracter excepto a, b, o c (negacion)
[a-zA-Z] Desde la a a la z o desde la A hasta la Z, incluidos
[a-d[m-p]] Desde la a hasta la d, o desde la m a la p: [a-dm-p] (union)
[a-z&&[def]] La d, la e, o la f (interseccion)
[a-z&&[^bc]] Desde la a hasta la z, excepto la b y la c: [ad-z] (resta)
[a-z&&[^m-p]] Desde la a hasta la z, excepto desde la m hasta la p: [a-lq-z](resta)
Intervalos de caracteres predefinidos

 

. Cualquier caracter (puede que no se incluyan los terminadores de linea)
d Un numero: [0-9]
D Todo menos un numero: [^0-9]
s Un espacio en blanco: [ x0Bf ]
S Todo menos un espacio en blanco: [^s]
w Una letra: [a-zA-Z_0-9]
W Todo menos letras: [^w]
Intervalos de caracteres POSIX
(solo para US-ASCII)

 

{lower} Letras minusculas: [a-z]
{upper} Letras mayusculas:[A-Z]
{alpha} Letras:[{lower}{upper}]
{digit} Numero decimal: [0-9]
{alnum} Caracter alfanumerico:[{alpha}{digit}]
{punct} Signos de puntuacion: uno de !"#$%&'()*+,-./:;<=>?@[]^_`{|}~
{graph} Los caracteres visibles: [{alnum}{punct}]
{print} Los caracteres imprimibles: [ {graph}]
{blank} Un espacio o un tabulador: [ ]
{cntrl} Un caracter de control: [x00-x1Fx7F]
{xdigit} Un numero hexadecimal: [0-9a-fA-F]
{space} Un espacio: [ x0Bf ]
Limites

 

^ Comienzo de una linea
$ Fin de una linea
 Fin de palabra
B No es fin de palabra
A El principio de la cadena de entrada
G El final del ultimo patron encajado
 El final de la entrada pero el terminador final, si existe
z El final de la cadena de entrada
Cuantificadores de cantidad

 

X? X, una o ninguna vez
X* X, cero o ninguna vez
X+ X, una o mas veces
X{n} X, exactamente n veces
X(n,} X, por lo menos n veces
X{n,m} X, por lo menos n veces pero no mas de m veces
Operadores logicos

 

XY X seguido de Y
X|Y X o Y
(X) X, como un grupo
Referencias hacia atras

 

Lo que haya encajado el nesimo grupo

, Escape, y entrecomillado

El caracter () sirve para preceder a expresiones con valores de escape tal y como se define en la tabla anterior, asi como para entrecomillar caracteres que de otra manera serian interpretados como caracteres de escape. De este modo la expresion representa a un unico y { representa a una llave.

Es un error usar un antes de cualquier caracter alfabetico que no corresponda a un caracter de escape, este tipo de construcciones se reservan para extensiones de futuras versiones del lenguaje de expresiones regulares. El caracter puede ser usado antes de un caracter no alfabetico a pesar de que el caracter sea parte de una expresion que no sea de escape.

Terminadores de linea

Un terminador de linea es una secuencia de uno o dos caracteres que indica el final de una linea de la secuencia de caracteres de entrada. Los siguientes son terminadores de linea:

  • El caracter de Nueva linea (line feed) (' '),
  • El caracter de retorno de carro seguido de una nueva linea (" "),
  • El caracter de retorno de carro unicamente (' '),
  • El caracter de nueva linea ('u0085'),
  • El caracter de separación de linea ('u2028'), o
  • El caracter de separacion de parrafos ('u2029).

Si el modo UNIX_LINES esta activado entonces los unicos terminadores de linea reconocidos seran caracteres de nueva linea.

La expresion regular . Representa a cualquier caracter excepto un terminador de linea a no ser que el modo DOTALL sea especificado.

Grupos

Los grupos se numeran contando los parentesis abiertos de izquierda a derecha. En la expresion ((A)(B(C))), por ejemplo, hay cuatro de estos grupos:

1 ((A)(B(C)))
2 (A)
3 (B(C))
4 (C)

El grupo cero siempre es la expresion completa.

COMPARTE ESTE ARTÍCULO

ENVIAR A UN AMIGO
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN GOOGLE +
¡SÉ EL PRIMERO EN COMENTAR!
Conéctate o Regístrate para dejar tu comentario.