Trabajar con Cadenas de Texto

Introducción

En el tiempo que llevo programando en PHP, he tenido la fortuna de no toparme con la tarea de procesar cadenas de texto muy complejas. Pero toda buena racha llega a su fin en algún momento.

Comencé a trabajar con una idea y pedido de Martín y sin dudas uno de los desafíos que me topaba era procesar una cadena de texto y encontrar en ella una URL, tomarla y reemplazarla el mismo texto pero con el vínculo generado.

Primeras ideas

Una de las cosas que aprendí de mi viejo, y él de su padre, es que Observando a los que sabe se aprende. Si eso lo trasladamos a Internet se podría decir que Leyendo a los que saben, se aprende.

Lo primero que se me vino a la mente fue Expresiones Regulares. Había leído ya en el blog de Fernando hace un tiempo las 10 razones para aprender y usar Expresiones regulares. Buscando un poco de información sobre el tema, me pareció lo más oportuno para solucionar mi problema.

Debo confesar que en un principio me costó bastante entender completamente como escribir la expresión regular que necesitaba. Consulté a un amigo de twitter fragilejunkie, también programador, quién me hizo llegar una explicación bastante clara de un tercer programador.

En si la expresión regular que armé fue bastante rudimentaria, no era necesario validar la URL, sólo encontrarla, lo que usé fue algo así:

$patron="@(http://)[a-zA-Z0-9_.-]+(/)?[a-zA-Z0-9_-?=&]*@";
preg_match_all($patron,$cadena,$resultados);

Esta era una forma rápida y bastante sucia de encontrar una URL. Las arrobas, son los delimitadores que necesita PHP para las expresiones regulares, luego lo primero que busca textualmente es http://, la siguiente porción de la cadena indica que a continuación DEBE haber un texto que puede contener letras, números, punto, guión bajo y guión medio. Para indicarle que debe estar esa porción del patrón, se utiliza el signo más que se ve luego de los corchetes.

Hasta este punto, podríamos identificar una URL con el siguiente formato: https://donpiluso.com.ar.

Aquí podríamos parar, pero generalmente la direcciones tiene más partes. Por eso a continuación aparece (/)?. Esto que significa: que puede aparece, o no, una barra. Luego le indico entre los corchetes el grupo de caracteres que pueden aparece o no, por eso en este caso usé un asterisco después del corchete como se ve a continuación: [a-zA-Z0-9_-?=&]*.

En este último caso incluí algunos caracteres adicionales como son el signo de interrogación, el ampersand, el guión medio (que se ve escapado para evitar problemas), el signo igual, etc. Hasta este punto podríamos identificar una URL con el formato: https://donpiluso.com.ar/https://donpiluso.com.ar/?p=XXX.

Pero a veces esto no es suficiente, a veces pueden aparecer direcciones URL un poco más largas, con lo cual terminé agregando un bloque incorporando además la / dentro de las posibilidades para esas direcciones muy largas que son raras, pero pueden aparecer.

¿Es la ideal?, tal vez no. Pero en las pruebas que realicé hasta el momento las direcciones se identificaron perfectamente. El script aún se encuentra en desarrollo, y sometido a pruebas, si llega a surgir algún otro detalle actualizaré el entrada más adelante.

preg_match_all($patron,$cadena,$resultados);

La línea que se lee arriba, realiza la comparación y guarda en la variable $resultados, todas las coincidencias que encontró. Después de realizar algunas pruebas y ver que se identificaban perfectamente, supuse que hacer los reemplazos sería simple.

Y en un punto, funcionó sin problemas, pero luego apareció un problema. En una de las cadenas de texto que se procesaron, aparecía dos veces una misma dirección URL, por lo tanto en la varible $resultados, tenía el mismo valor dos veces, el reemplazo se hacía dos veces y las URL quedaban mal formadas. Para mostrarlo gráficamente, lo que sucedía era lo siguiente. Antes de realizar el segundo reemplazo la cadena se encontraba de esta manera.

Estas leyendo el blog de DonPiluso <a href="https://donpiluso.com.ar">https://donpiluso.com.ar</a>. Muchas gracias por visitar <a href="https://donpiluso.com.ar">https://donpiluso.com.ar</a>

Cuando se realizaba el segundo reemplazo, no sólo se reemplazaba la etiqueta A con la dirección donde aparecía https://donpiluso.com.ar, sino tambíen dentro de las etiquetas A que se habían reemplazado anteriormente, quedando formado algo así:

Estas leyendo el blog de DonPiluso <a href="<a href="https://donpiluso.com.ar">https://donpiluso.com.ar</a>"><a href="https://donpiluso.com.ar">https://donpiluso.com.ar</a></a>

Sólo puse la primera apareción. Si observan dentro de la primera etiqueta A, el parámetro href termina siendo otra etiqueta A. A partir de ahí las direcciones nunca serían vínculos válidos.

La solución encontrada

La primera solución, que fue parcialmente válida, fue asegurarme que en el array de resultados quedaran sólo resultados únicos. ¿Por qué parcialmente válida?. Porque se podía dar el caso en que apareciera dos URL similares pero no idénticas. Supongamos un URL a este artículo del blog y otro al home del blog. Tendría el mismo problema que ya había pasado.

La solución que actualmente estoy usando y hasta el momento está respondiendo muy bien, fue tan lógica que demostró mi falta de práctica en el trabajo de cadenas. Dividir la cadena, analizar cada sección y luego reconstruirla.

$cadena=explode(" ",$cadena);
for($i=0;$i<count($cadena);$i++){
preg_match_all($patron,$cadena[$i],$resultados);
if(count($resultados[0])!=0){
$cadena[$i]="<a href="".$resultados[0][0]."">".$resultados[0][0]."</a>";
}
}
$cadena=implode(" ",$cadena);

Lo código anterior trabaja así. Explode divide la cadena por los espacios. Ahora en el variable cadena, tendré un array, que podré recorrer y comparar con mi expresión regular en cada uno de sus índices. Si hay algún valor en $resultados[0] (esto es, donde se guardan las coincidencias al total del patrón), realizo un remplazo simple, cambiando le contenido de ese índice. Cuando termina de recorrer el array, reconstruyo la cadena mediante implode.

De esta manera pude solucionar el problema, sin riesgo de doble reemplazos, ya que cada reemplazo se hace en un índice en particular del array que luego no se vuelve a recorrer con el mismo patrón. Y sin dudas me queda una experiencia más que importante ya que la próxima vez que deba analizar algo en una cadena, lo primero que haré será dividirla.

One Trackback

  • By Programming in Real Life on 13 mayo, 2010 at 5:39 pm

    Expresiones Regulares en MySQL…

    El Disparador Ayer rendí el primer práctico de Programación II. El tema a rendir era SQL, particularmente sentencias SELECT de distinta dificultad (Filtrar datos de una tabla, tomar datos de dos o más tablas, agrupar, operaciones con grupos, etc). Mien…

Post a Comment

Your email is kept private. Required fields are marked *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.