Expresiones regulares. Cómo trabajarlas con ASP.NET.

Como parte segunda del artículo sobre expresiones regulares, recordarte que en la primera parte "Expresiones regulares. Introducción", se dio una pequeña entrada a lo que eran las expresiones regulares, para qué podían servir y algunos enlaces buenos para profundizar en el tema. Ahora, lo que vamos a ver es cómo con ASP.NET se pueden tratar esas expresiones regulares de un modo muy sencillo.

Supongamos que queremos recoger todas las palabras de un texto que estén en negrita, es decir que estén entre los tags <b> y </b> (ya, ya sé que lo sabías ).

Por tanto, nuestra expresión regular sería: (?<=<b>).+(?=</b>). Es decir, que tenga como prefijo <b>, como sufijo </b> y entre ambos uno o más caracteres.

Como este simple código lo obtendríamos

busconegrita.aspx.cs
public MatchCollection buscarNegrito(string texto)
{
   string pattern = "(?<=<b>).+(?=</b>)";
   Regex myReg = new Regex(pattern, RegexOptions.IgnoreCase);

   if (myReg.IsMatch(texto))
   {
      MatchCollection resultadosBusqueda = myReg.Matches(texto);
   }
   else
   {
      return null;
   }

   return resultadosBusqueda;
}


Donde lo primero que hacemos es definir la expresión regular; podríamos habérsela pasado como atributo de la función, por lo que ésta nos serviría para cualquier expresión regular que definamos.

Seguidamente creamos la variable de tipo Regex, inicializándola con la propia expresión regular y con un segunda atributo muy útil y que nos ahorra algún que otro dolor de cabeza, que le dice a la variable Regex que no distinga entre mayúsculas y minúsculas.

A continuación comprobamos con myReg.IsMatch(texto) si nos va a devolver algún resultado. En este caso no es algo demasiado útil, pero en otras ocasiones podría servir.

Si es cierto que se nos va a devolver algún resultado, los recogemos todos en un MatchCollection, que no es más que una colección de resultados que más tarde podremos iterar. Como alternativa, podríamos haber cogido un único resultado con: 

   Match myMatch = myReg.Match(texto);
 
y luego ir iterando con:

   myMatch.NextMatch();

pero personalmente, si el eficiencia no es extremadamente necesaria, prefiero las colecciones, pues es mucho más cómodo e intuitivo trabajar con ellas.

Si por ejemplo la frase fuera:

El hombre es una <b>fracción</b>, donde el <b>numerador</b> es lo que es y el <b>denominador</b> lo que dice ser.

... myMatch tendría tres elementos: fracción, numerador y denominador.

Por tanto, ya sabemos inicializar una variable Regex, así como a ver si nos daría algún resultado. Pero lo más importante es ver que es muy sencillo recoger dichos resultados en una colección. Para no dejarse nada en el tintero, veamos cómo se puede trabajar con esa colección resultante:

trataResultados.aspx.cs
private bool trata(string texto)
{
   MatchCollection resultados = buscarnegrita(texto);
   foreach (Match resultado in resultados)
   {
      if (Match.Value == "subgurim")
      {
         return true;
      }
   }
   return false;
}


Donde simplemente buscamos lo que esté en negrita dentro de un texto, y si alguno de los resultados es "subgurim" nos devuelve verdadero, y si no es así, falso.

Así pues, con esto ya podríamos trabajar, pero ASP.NET sigue dándonos más opciones tremendamente útiles.

Por ejemplo, si trabajamos con grupos de expresiones regulares (si no sabes lo que son los grupos de expresiones regulares, te recomiendo que le eches un vistazo a tutoriales como "The 30 minutes regex tutorial" o en www.regular-expressiones.info), podemos trabajarlas muy cómodamente con:

- GetGroupNames(): devuelve un array de strings con los nombres que le hemos dado a los grupos.
- GetGroupNumbers(): nos devuelve un array de enteros con los número que se corresponden a los grupos
- GroupNameFromNumber(0): donde le damos un entero y nos devuelve un string con el nombre del grupo cuyo índice se corresponde con el entero.
- GroupNumberFromName("nombredelgrupo"): al contrario de la anterior, le damos el nombre del grupo y nos devuelve el entero.

Para finalizar, hacer mención a otras dos funciones muy útiles y a las que se les puede sacar muchísimo provecho, son:
- Replace(...).
- Split(...).

Pongamos un ejemplo de ambas:

ejemploReplaceRegex.aspx.cs
public void reemplazar(string texto)
{
   string pattern = "(?<=<b>).+(?=</b>)";
   Regex myReg = new Regex(pattern, RegexOptions.IgnoreCase);

   texto = myReg.Replace(texto, "estoy en negrita");
}


Donde, como en el ejemplo anterior, definimos que nuestro pattern es "todo lo que esté en negrita sin incluir los tags <b> y </b>", y con el replace, reemplazamos de la variable texto los resultados de ese pattern por el texto "estoy en negrita".

De modo que la frase

"¿Quieres ser <b>rico</b>? Pues <b>no te afanes</b> en aumentar tus bienes sino en <b>disminuir tu codicia</b>. Epicuro."

... pasaría a ser...

"¿Quieres ser <b>estoy en negrita</b>? Pues <b>estoy en negrita</b> en aumentar tus bienes sino en <b>estoy en negrita</b>. Epicuro.".

¡¡¡Imaginaos la que habría que armar para hacer esto con el método Replace(...) de las variables string!!!

La misma función Replace de Regex, admine un par de parámetros más, el típico "count" en el que le dices el máximos de reemplazos que debe hacer, y "startat", en el que le indicas a partir de dónde quiere que hagas los reemplazos.


En cuanto a Split(...), es algo más sencillo:

ejemploSplitRegex.aspx.cs
public string[] mysplit(string texto)
{
   string pattern = "<br>|<br />";
   Regex myReg = new Regex(pattern, RegexOptions.IgnoreCase);

   return myReg.Split(texto);
}


Donde en este caso, el pattern indica que queremos encontrar todas las referencias a "<br>" y a "<br/>". La función devuelve un array de strings, donde el delimitador ha sido cualquiera de esas dos variables.

Esto mismo también se podría hacer con la función Split(...) de las variables string, pero el límite del Split de Regex sólo lo pone tu imaginación, y el del Split() de string lo pone tu paciencia .


Un último apunte, también se pueden hacer todas estas operaciones apoyándonos en las funciones estáticas del propio tipo Regex, por lo que no haría falta inicializar ninguna variable.

Por ejemplo, podríamos hacer

Regex.(texto, "<br>|<br />", RegexOptions.IgnoreCase);

en lugar de todo el código del último ejemplo.

El hacerlo de un modo u otro depende de muchas cosas, como el modo de proceder habitual de cada uno o la situación de código en la que te encuentres.


Como conclusión, os aconsejo no perdáis nunca de vista a Regex y que lo utilicéis tanto como podáis si la situación lo requiere.