El Codiguero
Programando para la wé

Avatar de alvlin Publicado por alvlin, el 19/09/2008
Categorías: Programación

Cuantificadores

Parte 6 de 9 de la serie Expresiones Regulares

Introducción

He aquí el último tema de la serie básica sobre Expresiones Regulares: los cuantificadores.
Ciertos metacaracteres cumplen la función de indicar cuántas veces debe aparecer un patrón en una cadena para que la comparación sea exitosa. Estos caracteres se denominan cuantificadores, y son los siguientes:

* (asterisco)
Indica que el patrón debe aparecer cero o más veces
+ (más)
Indica que el patrón debe aparecer una o más veces
? (interrogación)
Indica que el patrón debe aparecer cero o una vez. Puede decirse que indica que el patrón es opcional (puede aparecer o no).

¿Cómo se usan?

Los cuantificadores deben ir a la derecha del trozo de patrón al que deben aplicarse. Por defecto se aplican a la clase de caracteres que los precede, pero su efecto puede extenderse usando los paréntesis.
A continuación algunos ejemplos simples para dejar claro este punto:

[a-z]+ : "una letra o más". Coincide con cualquier palabra.

1[a-z]? : "Un 1 seguido o no por una letra". Coincide con "1", "1a", "1b" pero no con "1ab" (dado que ? indica que debe aparecer solamente 0 o 1 letra)

lab* : "l, a, seguidas de 0 o más b". Coincide con "la", "lab", "labbbbbbbbbbb", etc.

l(ab)*: "l seguida de 0 o más secuencias ab". Coincide con "l", "lab", "lababababab", etc.

Las llaves

Existe otro tipo de cuantificadores: las llaves ({, }). Se aplican de la misma manera que los otros cuantificadores (a continuación del patrón a cuantificar), y se utilizan para controlar la cantidad exacta de veces que el patrón debe aparecer.
La sintaxis es
{min,max}

Que quiere decir que el patrón debe aparecer "como mínimo min veces, y como máximo max veces". Tanto min como max son opcionales, cuando no aparece min se toma "mínimo cero veces" y cuando no aparece max se toma "cualquier cantidad de veces". De esta forma, se pueden escribir los demás cuantificadores con la sintaxis de llaves:

Cuantificador Equivale a
? {0,1}
* {0,}
+ {1,}

Las llaves también pueden usarse para indicar la cantidad exacta de veces que debe aparecer el patrón, para esto se omite la coma y se da un solo valor entre las llaves:
{num_veces}

Ejemplo del uso de llaves

Como ejemplo del uso de llaves, se usarán para un patrón similar al patrón dado como ejemplo en la tercera parte de esta serie: Exp. Regulares: Clases de caracteres
^[0-3][0-9][-/][0-1][0-9][-/][0-2][0-9][0-9][0-9]$

En este caso se considerará de la siguiente forma:
^[0-9][0-9][-/][0-9][0-9][-/][0-9][0-9][0-9][0-9]$
Es decir, simplemente "dos números seguidos de una barra o un guión, otros dos números separados por otra barra, y cuatro números al final". Para ejemplificar mejor el uso de las llaves, se permite que el año se exprese con solamente dos dígitos:
^[0-9]{2}[-/][0-9]{2}[-/][0-9]{2,4}$
(este último patrón tiene el efecto desagradable de permitir que el año se represente con tres dígitos, pero como ejemplo funciona).

Si las explicaciones dadas hasta ahora fueron lo suficientemente claras, debería ser trivial la siguiente simplificación (o al menos entendible):
^([0-9]{2}[-/]){2}[0-9]{2,4}$
Que utiliza los paréntesis para delimitar el efecto del segundo "{2}".

Un ejemplo más complejo

A través de las partes de esta serie, se han explicado todos los elementos básicos que conforman la sintaxis de las expresiones regulares. Es hora de un ejemplo complejo que utilice varios de estos elementos.

Quizás sea más útil como ejercicio que como ejemplo: crear un patrón que coincida con '1 de Mayo', '1ero de Mayo', '1 May', o incluso "Primero de Mayo".

Los pasos a la hora de crear un patrón son siempre similares: es necesario localizar las partes comunes en las cadenas contra las que se va a comparar. Luego hay que trabajar con cada una de estas partes por separado.

En principio puede verse que las 4 cadenas comienzan con 1, 1ero o Primero. En la parte 4 (Paréntesis, escape de caracteres, y alternación de patrones) se explicó el uso del caracter de alternación, que es el que funciona en este caso:
1|1ero|Primero de Mayo
Pero si se escribe de la forma anterior, el patrón coincidirá con "1", con "1ero" y con "Primero de Mayo". Se hace necesario limitar la acción de los |:
(1|1ero|Primero) de Mayo
Con esto quedan cubiertas 3 de las 4 posibilidades.
Siguiente problema: la palabra "de" no está en una de las cuatro cadenas, por lo que debe ser "opcional". Se puede indicar que "de" es opcional usando el ? (ver más arriba):
(1|1ero|Primero)( de)? Mayo
Aquí vale hacer dos observaciones. La primera es que se usan los paréntesis para indicar que la cadena completa " de" es opcional. Si se utilizara de?, lo opcional sería solamente la letra "e".
La segunda observación es que se debe recordar que el espacio es un caracter como cualquier otro, por lo que debe ser incluido en los patrones cuando se lo requiere. Por eso se utiliza "( de)" y no solamente "(de)".
Para finalizar, simplemente se necesita que el patrón coincida tanto con "Mayo" como con "May", por lo que puede utilizarse de nuevo el caracter de alternación:
(1|1ero|Primero)( de)? (May|Mayo)
Este patrón coincide con las cadenas propuestas, y también con otras que no tienen la forma correcta (por ejemplo, "Primero May").
Quizás sea una buena forma de ilustrar que las expresiones regulares solamente pueden validar forma, no contenido. Para validar contenido se deben utilizar más patrones (uno por cada contenido posible) o algo de lógica en el programa (recordar que las expresiones regulares se utilizan mayormente en el contexto de un programa, no por sí solas).

Otro punto interesante es que muchas veces hay varias formas de lograr el mismo efecto, con patrones diferentes. Todo depende de qué tanto se pueda "factorizar" la cadena buscada, qué tanta experiencia se tenga con las expresiones regulares, y qué tanta tolerancia ante los errores se tiene.
Por ejemplo, en el patrón anterior pueden quitarse varias apariciones de | y sustituirse por ?:
(1(ero)?|Primero)( de)? Mayo?
(recordando que, al no haber paréntesis para delimitar el efecto del último ?, éste se aplica solamente a la "o".)

Este último patrón acepta los mismos errores que el anterior (por ejemplo, aceptará "1 de May"). Si este punto no es importante, podría reducirse aún más:
(1|Prim)(ero)?( de)? Mayo?
Introduciendo un nuevo error, ahora "Prim de Mayo" también validará.

El modificador ?

Una característica a veces molesta de las expresiones regulares es que los cuantificadores tienen por defecto un comportamiento "avaricioso": intentan coincidir con la mayor cadena posible.
Este comportamiento por defecto conlleva problemas si no se tiene en cuenta.

Ejemplo

Se tiene la cadena
<p>Hola, soy <strong>un párrafo</strong> en <strong>HTML</strong></p>
Y se desea obtener los trozos dentro de las etiquetas strong. En principio podría usarse el patrón
<strong>(.*)<strong>
Pero por el comportamiento "avaricioso" por defecto, el subpatrón ".*" coincide con:
un párrafo</strong> en <strong>HTML
en vez de coincidir dos veces con el contenido de cada etiqueta strong.

El modificador ? colocado inmediatamente después del cuantificador obliga a que éste se comporte de otra forma: ahora coincidirá con la menor cadena posible, no con la mayor.
<strong>(.*?)<strong>
Produce como resultado los dos trozos de cadena por separado:
un párrafo HTML

Aquí finaliza el tutorial "teórico" de Expresiones Regulares. Recomiendo tener cerca esta parte y las anteriores a la hora de leer las partes siguientes, que se centran en el uso de las Expresiones Regulares con varios lenguajes de programación.

  • Digg
  • del.icio.us
  • Meneame
  • Reddit
  • Technorati
  • StumbleUpon
  • Facebook
  • LinkedIn

» 1 Comentario para “Cuantificadores”

  1. Gastón escribió:

    Excelente!… me encantaron estas series sobre las expresiones regulares… muy entendible y práctico.

    Buen blog :D

    Aqui tienes un lector nuevo.

» Dejá una respuesta



Todo el contenido de este sitio está bajo una licencia de Creative Commons.

Campaña AnyBrowser | XHTML 1.0 Válido | CSS 2 Válido | WAI A

Diseño creado por alvlin. Sitio basado en WordPress