Expresiones Regulares codiciosas y perezosas

Al empezar a utilizar expresiones regulares, una de las primeras situaciones con las que me topé, es la de que algunos de los operadores cuantificadores poseen cierto comportamiento como el de ser perezosos o codiciosos al momento de hacer ‘match’ con la cadena especificada. Antes de explicar de que se trata este comportamiento, recordemos los operadores cuantificadores:

  • *‘ indica que la expresión puede venir cero o más veces,
  • +‘ indica que la expresión puede venir una o más veces,
  • ?‘ indica que la expresión puede o no venir.

Empecemos la explicación con un ejemplo escrito en php y haciendo uso de la librería de expresiones regulares de php: PCRE (Compatibles con Perl).

Supongamos que tenemos analizar un código html en el cual nos interesa extraer el texto que está contenido entre etiquetas ‘div’. El código html de ejemplo es :

<div>esto es un ejemplo de texto</div> <div> se mostrara como funcionan en modo perezoso o codicioso</div>

Para extraer el texto contenido en el div usamos la siguiente expresión regular: <div>(.*)</div>
que utilizando php la definimos así:

$patron = '/<div>(.*)<\/div>/';

Utilizaremos la función preg_match_all para obtener las cadenas que concuerden con la expresión regular propuesta. El script se vería de la siguiente manera:

<?php
$texto = "<div>esto es un ejemplo de texto</div> <div> se mostrara como funcionan en modo perezoso o codicioso</div>";
$patron = '/<div>(.*)<\/div>/U';
preg_match_all ($patron, $texto, $matches);
echo "<pre>";
print_r($matches);
echo "</pre>";
?>


Si ejecutamos el script, podemos ver que la cadena que hace match es:

[0] => 'esto es un ejemplo de texto</div><div> se mostrara como funcionan en modo perezoso o codicioso'.

Pero este resultado no es el que esperabamos. ¿Qué pasó aquí? Es ahora que podemos hablar del cuantificador codicioso, el cual trata de hacer match a la cadena más grande que encuentre. Y eso fue lo que pasó con el script, hizo match desde la apertura de la etiqueta ‘<div>’ y debido al operador (.*) incluyó todo el texto intermedio sin detenerse en el primer cierre ‘</div>’, ya que siguió buscando si encontraba otro cierre de la etiqueta, el cual lo encontró al final de la cadena.

Entonces ¿cómo hacemos para que obtener el texto dentro de cada etiquetas ‘div’? Transformado el cuantificador codicioso en un cuantificador perezoso para que haga match con la cadena más corta que encuentre. Y eso se puede hacer agregando ‘?’ despues de la expresion (.*). La expresión regular quedaría entonces de la siguiente manera:

<div>(.*?)</div>

Reescribiendo el script:

<?php
$texto = "<div>esto es un ejemplo de texto</div> <div> se mostrara como funcionan en modo perezoso o codicioso</div>";
$patron = '/<div>(.*?)<\/div>/';
preg_match_all ($patron, $texto, $matches);
echo "<pre>";
print_r($matches[1]);
echo "</pre>";
?>


Lo ejecutamos y vemos que ahora ya nos devuelve unicamente el contenido que esta dentro de cada etiqueta ‘div’:

Array
(
[0] => esto es un ejemplo de texto
[1] => se mostrara como funcionan en modo perezoso o codicioso
)

Este mismo comportamiento se aplica a los demás cuantificadores:

CodiciosoPerezoso
(.*)(.*?)
(.+)(.+?)
(?)(??)

Asi que ahora podemos usarlos de manera codiciosa o perezosa según nos convenga.

Como información adicional, las expresiones regulares PCRE de php nos ofrecen el modificador U (PCRE_UNGREEDY) para poder alterar este comportamiento, haciendo que los cuantificadores que son codiciosos de manera predeterminada, se vuelvan perezosos. Usando este modificador el patrón quedaría así:

$patron = '/<div>(.*)<\/div>/U';

Y eso es todo amigos… hasta la próxima.

Los comentarios están cerrados.