Metacaracteres de rango, alternancia y agrupadores

Seguimos con las expresiones regulares, con los metacaracteres de rango y los metacaracteres de alternancia y agrupadores.
Metacaracteres de rango

Los corchetes [] incluidos en un patrón permiten especificar el rango de caracteres válidos a comparar. Basta que exista cualquiera de ellos para que se de la condición:

<?

[abc] // El patrón coincide con la cadena si en esta hay
// cualquiera de estos tres carácteres: a, b, c

[a-c] // coincide si existe una letra en el rango ("a", "b" o "c")
c[ao]sa // coincide con casa y con cosa

[^abc] // El patrón coincide con la cadena si en esta NO hay
// ninguno de estos tres carácteres: a, b, c
// Nota que el signo ^ aqui tiene un valor excluyente

c[^ao]sa // Coincide con cesa, cusa, cisa (etc); no coincide
// con casa ni cosa

[0-9] // Coincide con una cadena que contenga cualquier
// número entre el 0 y el 9

[^0-9] // Coincide con una cadena que NO contenga ningun
// número

[A-Z] // Coincide con cualquier carácter alfabetico,
// en mayúsculas. No incluye numeros.

[a-z] // Como el anterior, en minúsculas

[a-Z] // Cualquier carácter alfabético, mayusculas o minusculas

?>


Una cuestión a recordar es que las reglas de sintaxis de las expresiones regulares no se aplican igual dentro de los corchetes. Por ejemplo, el metacarácter ^ no sirve aqui de ancla, sino de caracter negador. Tampoco es necesario escapar todos los metacaracteres con la barra invertida. Solo será necesario escapar los siguientes metacaracteres: ] \ ^ -

El resto de metacaracteres pueden incluirse ya que son considerados -dentro de los corchetes- caracteres normales.

patrón: [aeiou]
el ala aleve del leve abanico

patrón: [^aeiou]
el ala aleve del leve abanico

patrón: [a-d]
el ala aleve del leve abanico

Como estos patrones se usan una y otra vez, hay atajos:

<?
// atajo equivale a significado

\d [0-9] // numeros de 0 a 9
\D [^0-9] // el contrario de \d

\w [0-9A-Za-z] // cualquier numero o letra
\W [^0-9A-Za-z] // contrario de \w, un carácter que no

// sea letra ni numero

\s [ \t\n\r] // espacio en blanco: incluye espacio,
// tabulador, nueva linea o retorno

\S [^ \t\n\r] // contrario de \s, cualquier carácter
// que no sea espacio en blanco

// solo regex POSIX
[[:alpha:]] // cualquier carácter alfabético aA - zZ.
[[:digit:]] // Cualquier número (entero) 0 - 9
[[:alnum:]] // Cualquier carácter alfanumérico aA zZ 0 9
[[:space:]] // espacio
?>


Metacaracteres de alternancia y agrupadores

<?
(xyz) // coincide con la secuencia exacta xyz
x|y // coincide si esta presente x ó y

(Don|Doña) // coincide si precede "Don" o "Doña"
?>


patrón: (al)
el ala aleve del leve abanico

patrón: a(l|b)
el ala aleve del leve abanico
Los paréntesis sirven no solamente para agrupar secuencias de caracteres, sino también para capturar subpatrones que luego pueden ser devueltos al script (backreferences). Hablaremos mas de ello al tratar de las funciones POSIX y PCRE, en las paginas siguientes.

Un ejemplo típico sería una expresion regular cuyo patrón capturase direcciones url validas y con ellas generase links al vuelo:

<? $text = "una de las mejores páginas es http://www.blasten.com";
$text = ereg_replace("http:\/\/(.*\.(com|net|org))",
"\1", $text);
print $text;
?>

El anterior ejemplo produciría un enlace usable, donde la url se tomaría de la retro-referencia \0 y la parte visible de la retro-referencia \1 una de las mejores páginas es www.ignside.net

Fijate que en el ejemplo anterior usamos dos grupos de parentesis (anidados), por lo que se producirian dos capturas: La retro-referencia \0 coincide con la coincidencia buscada. Para capturarla no es preciso usar parentesis.

La retro-referencia \1 coincide en este caso con "www.blasten.com" y es capturada por el parentesis (.*\.(com|net|org))

La retro-referencia \2 coincide con "net" y se corresponde con el parentesis anidado (com|net|org)

Ten en cuenta que esta caracteristica de capturar ocurrencias y tenerlas disponibles para retroreferencias consume recursos del sistema. Si quieres usar parentesis en tus expresiones regulares, pero sabes de antemano que no vas a reusar las ocurrencias, y puedes prescindir de la captura, coloca despues del primer parentesis ?:

<?
text = ereg_replace("http:\/\/(.*\.(?:com|net|org))",
       "<a href=\"\0\">\1</a>", $text);
?>


Al escribir (?:com|net|org) el subpatron entre parentesis sigue agrupado, pero la coincidencia ya no es capturada.

Como nota final sobre el tema, PHP puede capturar hasta 99 subpatrones a efectos de retro-referencia, o hasta 200 (en total) si buscamos subpatrones sin capturarlos.