<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Medel&#039;s ramblings &#187; expresiones regulares</title>
	<atom:link href="http://ramblings.luismedel.com/tag/expresiones-regulares/feed/" rel="self" type="application/rss+xml" />
	<link>http://ramblings.luismedel.com</link>
	<description>El blog de Luis Medel</description>
	<lastBuildDate>Thu, 27 May 2010 14:50:45 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Expresiones regulares. Curso de guerrilla</title>
		<link>http://ramblings.luismedel.com/2009/06/expresiones-regulares/</link>
		<comments>http://ramblings.luismedel.com/2009/06/expresiones-regulares/#comments</comments>
		<pubDate>Wed, 10 Jun 2009 11:00:04 +0000</pubDate>
		<dc:creator>Luis Medel</dc:creator>
				<category><![CDATA[Artículos]]></category>
		<category><![CDATA[Desarrollo]]></category>
		<category><![CDATA[expresiones regulares]]></category>

		<guid isPermaLink="false">http://ramblings.luismedel.com/?p=140</guid>
		<description><![CDATA[Si has trabajado alguna vez desde la línea de comandos seguro que has tenido la necesidad de manipular un grupo de ficheros cuyo nombre cumpliese una determinada condición (*.txt, balance_200?.xls, &#8230;), ¿cierto? Entonces ya sabrás lo que son los comodines y lo sencillo que es utilizarlos. Pues bien, cuando necesitamos hacer una búsqueda sobre un texto [...]]]></description>
			<content:encoded><![CDATA[<p>Si has trabajado alguna vez desde la línea de comandos seguro que has tenido la necesidad de manipular un grupo de ficheros cuyo nombre cumpliese una determinada condición (*.txt, balance_200?.xls, &#8230;), ¿cierto? Entonces ya sabrás lo que son los <strong>comodines</strong> y lo sencillo que es utilizarlos.</p>
<p>Pues bien, cuando necesitamos hacer una búsqueda sobre un texto (en nuestro caso, el fuente de un programa) estamos acostumbrados normalmente a introducir el valor concreto que queremos localizar.</p>
<p>Imaginemos que tenemos varias funciones que se llaman de manera similar (concatenarFechas(), concatenarCadenas(), concatenarListas()&#8230;) Si queremos buscar dónde aparece alguna de estas funciones no tenemos más que buscar de manera literal el texto correspondiente a su nombre. Por ejemplo, &#8220;concatenarListas&#8221;. Pero, ¿qué ocurre si queremos buscar las apariciones de <strong>todas</strong> las funciones que se llamen de manera similar a la vez? ¿tenemos algo parecido a los comodines para buscar texto? Efectivamente, sigue leyendo <img src='http://ramblings.luismedel.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Una <strong>expresión regular</strong> es, valga la redundancia, una expresión que define un <strong>patrón de búsqueda</strong> sobre un texto. En el ejemplo anterior, la búsqueda literal de la función concatenarListas() equivaldría a la expresión regular /concatenarListas/. Por otro lado, la búsqueda de todas las funciones que se llamen de manera similar podría llevarse a cabo con la expresión /concatenar[A-Z][a-zA-Z0-9]+/. De momento no te preocupes por entenderlo porque vamos a empezar desde el principio <img src='http://ramblings.luismedel.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h2>Literales</h2>
<p>Como hemos comentado antes, una expresión regular puede ser cualquier literal. Así, ante el texto (olvidemos los acentos para simplificar los ejemplos)&#8230;</p>
<p style="text-align: center;">mi mama me ama, mi mama me mima</p>
<p>&#8230;la expresión /m/ (el literal &#8220;m&#8221;) nos devolvería cada una de las coincidencias con &#8220;m&#8221; del texto (11 en total). Si utilizamos la expresión /mama/ obtendremos dos resultados (las dos apariciones de la palabra &#8220;mama&#8221;). Y si utilizamos la expresión /ama/, ¿cuántas coincidencias obtendremos? ¿Una? No. Tres, ya que la palabra &#8220;mama&#8221; también coincide con el patrón /ama/.</p>
<p>Pero las expresiones regulares servirían de poco si su utilidad terminase en la búsqueda de simples literales. ¿Seguimos?</p>
<h2>El comodín</h2>
<p>El carácter &#8220;.&#8221; es un carácter comodín que podría leerse como <em>&#8220;cualquier carácter&#8221;</em>. El patrón /m.ma/ encontraría cualquier resultado de 4 caracteres que comenzase por &#8220;m&#8221; y terminase por &#8220;ma&#8221;, sin importar cuál fuese el segundo carácter (letra, número, símbolo, espacio en blanco&#8230;)</p>
<p>Si buscamos una equivalencia con los comodines que solemos usar en la línea de comandos, equivaldría al comodín &#8220;?&#8221;. Es decir, <em>cualquier carácter, pero sólo uno</em>.</p>
<h2>Conjuntos</h2>
<p>Si en una posición concreta queremos buscar más de un carácter, podemos especificar un conjunto de caracteres válidos. Para ello, no tenemos más que indicar los caracteres deseados entre corchetes.</p>
<p>Por ejemplo, el patrón /m[ai]ma/ obtendría resultados tanto con la palabra &#8220;mama&#8221; como con la palabra &#8220;mima&#8221;. El orden en el que se defina el conjunto no tiene importancia.</p>
<p>También podemos especificar <strong>rangos de caracteres</strong> que evitarán que tengamos que escribir todos y cada uno de los caracteres que queramos buscar. Así, si queremos aceptar cualquier letra minúscula del abecedario podemos usar el rango [a-z], si lo que queremos es aceptar las mayúsculas deberíamos usar el rango [A-Z] y si queremos aceptar cualquier carácter numérico el rango [0-9].</p>
<p>Por supuesto, como es de esperar, los rangos se pueden combinar entre sí. Así, el patrón /[a-z0-9]/ representa un carácter (recuerda que el conjunto representa la búsqueda de <strong>un sólo carácter</strong>) que puede ser tanto una letra minúscula como un carácter numérico. De manera similar, el patrón /[a-fA-F0-9]/ localizaría un carácter hexadecimal (cualquier carácter en los rangos 0..9 y A..F).</p>
<p>Ten en cuenta que tenemos la libertad al establecer los límites de los rangos. Si sólo necesitamos capturar las 5 primeras letras del abecedario, podemos indicarlo con el conjunto [a-e]. De manera análoga podemos definir cualquier rango numérico.</p>
<p>Si lo que deseamos es <strong>negar el conjunto</strong>, esto es, realizar una búsqueda de caracteres que <strong>no estén</strong> en el conjunto especificado, debemos añadir al principio del mismo el carácter &#8220;^&#8221;. El patrón /m[^i]na/ devolvería resultados con &#8220;mona&#8221;, &#8220;mena&#8221;, &#8220;mana&#8221;&#8230;pero no devolvería &#8220;mina&#8221;, ya que hemos excluido ese carácter de manera explícita.</p>
<h2>Atajos -que no &#8220;hatajos&#8221; <img src='http://ramblings.luismedel.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </h2>
<p>Dado que incluso los patrones de tamaño medio pueden llegar a ser bastante complejos de interpretar visualmente, existen algunos atajos para los rangos más comunes. Por ejemplo, la expresión \d encuentra cualquier dígito (igual que el rango [0-9]) y \w encuentra cualquier carácter alfanumérico, incluido el guión bajo (igual que el rango [A-Za-z0-9_])</p>
<p>A continuación dejo una pequeña lista con algunos atajos que se puedes utilizar.</p>
<p>\d &#8211; Captura cualquier dígito. Equivale a [0-9]</p>
<p>\D &#8211; Captura cualquier carácter que no sea un dígito. Equivale a [^0-9]</p>
<p>\s &#8211; Captura cualquier carácter que sea un espacio en blanco. Equivale a [ \t\n\r]</p>
<p>\S &#8211; Captura cualquier carácter que no sea espacio en blanco . Equivale a [^ \t\n\r]</p>
<p>\w &#8211; Captura cualquier carácter alfanumérico. Equivale a [a-zA-Z0-9_]</p>
<p>\W &#8211; Captura cualquier carácter no alfanumérico. Equivale a [^a-zA-Z0-9_]</p>
<h2>Caracteres especiales</h2>
<p>Como habrás podido comprobar, existe un conjunto de caracteres que tiene significado especial en la sintaxis de las expresiones regulares ¿cómo hacemos entonces para &#8220;capturar&#8221; uno de esos caracteres en una expresión regular? Sencillo. Se deben escapar anteponiéndoles una barra invertida &#8220;\&#8221;.</p>
<p>Esos caracteres son los siguientes:</p>
<pre style="text-align: center;">^$|=#!&lt;&gt;-.,*?+[](){}\</pre>
<p>Su significado lo descubrirás a lo largo de las secciones siguientes.</p>
<h2>Repetición y cuantificadores</h2>
<p>Hasta ahora hemos visto expresiones muy sencillas y con una longitud determinada. Hemos visto antes un patrón que localizaba un sólo carácter hexadecimal (/[a-fA-F0-9]/). Sin embargo, ¿cómo hacemos para localizar números hexadecimales completos con múltiples caracteres?</p>
<p>La repetición de caracteres se expresa añadiendo tras el carácter que se desea repetir, alguno de los caracteres modificadores &#8220;*&#8221;, &#8220;+&#8221; y &#8220;?&#8221;.</p>
<p>El más intuitivo de los tres es &#8220;*&#8221; que se leería como &#8220;ninguna o varias veces&#8221;. El patrón /ab*c/ encontraría &#8220;ac&#8221;, &#8220;abc&#8221;, &#8220;abbc&#8221;, &#8220;abbbbb &#8230; bbbbbc&#8221;, etc.</p>
<p>Por otro lado, tenemos el carácter &#8220;+&#8221; que se leería como &#8220;al menos una vez&#8221;. El patrón /ab+c/ encontraría &#8220;abc&#8221;, &#8220;abbc&#8221;, &#8220;abbbbb &#8230; bbbbbc&#8221;, etc. pero *nunca* encontraría &#8220;ac&#8221;.</p>
<p>Por último tenemos el carácter &#8220;?&#8221; se leería como &#8220;una o ninguna&#8221;. El patrón /ab?c/ sólo encontraría &#8220;ac&#8221; y &#8220;abc&#8221;.</p>
<p>Como se puede apreciar, el modificador sólo afecta al carácter (o conjunto de ellos) que tiene inmediatamente a su izquierda.</p>
<p>Con esto que acabamos de aprender, ya sí estamos en condiciones de escribir un buen patrón para números hexadecimales /[a-fA-F0-9]+/. Si queremos aceptar números hexadecimales al estilo de C (precedidos de &#8220;0x&#8221;) podemos usar el patrón /0x[a-fA-F0-9]+/</p>
<p>Hay otras ocasiones en las que no interesa que la repetición sea infinita (como ocurre con &#8220;*&#8221; y &#8220;+&#8221;) y desearemos especificar un número máximo (o mínimo) de repeticiones. Para eso tenemos los modificadores &#8220;{num}&#8221; y &#8220;{min,máx}&#8221;. Al igual que los anteriores modificadores, se debe indicar a la derecha del carácter (o conjunto) que deseamos repetir.</p>
<h3>Algunos ejemplos</h3>
<p>/0x[a-fA-F0-9]{4}/ encuentra números con 4 cifras hexadecimales.</p>
<p>/0x[a-fA-F0-9]{4,}/ encuentra números con <strong>al menos</strong> 4 cifras hexadecimales.</p>
<p>/0x[a-fA-F0-9]{4,8}/ encuentra números de entre 4 y 8 cifras hexadecimales.</p>
<p>Fíjate que &#8220;{0,}&#8221; equivale a &#8220;*&#8221; y que &#8220;{1,}&#8221; equivale a &#8220;+&#8221;.</p>
<h2>Agrupación</h2>
<p>Las expresiones de repetición que acabamos de ver, como ya he dicho, operan sobre el carácter situado inmediatamente a la izquierda. Entonces, ¿qué hacer si queremos que la repetición o cuantificación afecte a un grupo de caracteres? Para eso utilizamos la agrupación con paréntesis. Así, si queremos encontrar todas las coincidencias del literal &#8220;abcd&#8221; repetido 2 o más veces, podríamos usar la siguiente expresión /(abcd){2,}/. Con esto, encontraríamos &#8220;abcdabcd&#8221;, &#8220;abcdabcdabcd&#8221;, etc. pero no &#8220;abcd&#8221;.</p>
<h2>Capturas al inicio y al fin de la línea</h2>
<p>Todas las expresiones regulares que he escrito hasta el momento devolverían cualquier resultado que se encontrasen a lo largo de un texto sobre el que se lanzasen. En ciertas ocasiones resulta útil poder especificar si queremos que la captura ocurra al principio o al final del texto. Para esto disponemos de los caracteres &#8220;^&#8221; y &#8220;$&#8221;.</p>
<p>El carácter &#8220;^&#8221; sólo puede ir <strong>al principio</strong> de una expresión regular e indica que lo que vaya a continuación <strong>debe ocurrir al principio</strong> del texto. O dicho de otra manera, el texto <strong>debe comenzar</strong> con la expresión que indiquemos a continuación. Se verá muy claro con un ejemplo.</p>
<p>Por cierto, no hay que confundir este &#8220;^&#8221; con el operador de negación utilizado <strong>dentro</strong> de los conjuntos. Aunque se utilice el mismo carácter, su significado es completamente distinto.</p>
<p>Bien, dicho lo anterior, veamos un ejemplo sencillo. La expresión /^Luis/ devolverá resultados para las cadenas &#8220;Luis&#8221; y &#8220;Luis Medel&#8221;, pero no para &#8220;Me llamo Luis&#8221;, ya que &#8220;Luis&#8221; no aparece al principio del texto.</p>
<p>Por su parte, el carácter &#8220;$&#8221; sólo se puede utilizar <strong>al final</strong> de una expresión y sirve para requerir que lo que hayamos especificado justo a la izquierda debe ocurrir al final del texto (es decir, obligamos a que el texto termine con la expresión situada a la derecha del &#8220;$&#8221;)</p>
<p>Así, la expresión /Luis$/ devolverá resultados para &#8220;Luis&#8221; y &#8220;Me llamo Luis&#8221;, pero no para &#8220;Luis Medel&#8221;.</p>
<p>Gracias a estos dos caracteres resulta muy sencillo utilizar expresiones regulares para validar datos de entrada.</p>
<p>Por ejemplo, supongamos que queremos comprobar si una expresión introducida por un usuario se trata de un número entero compuesto única y exclusivamente por caracteres válidos (nada de espacios ni de otro tipo de caracteres) podemos utilizar la expresión /^\d+$/.</p>
<p>Si lo que queremos es comprobar si se trata de un número real válido, podemos usar la expresión /^\d+(\.\d+)?$/ (un número seguido <strong>opcionalmente</strong> por un punto y los decimales)</p>
<h2>Un ejemplo real</h2>
<p>Con lo que sabemos de expresiones regulares, ya estamos listos para ver un pequeño ejemplo real de las mismas ¿Cuántas veces has escrito una función de validación para direcciones de email?</p>
<p>Podemos hacerlo fácilmente con una expresión regular.</p>
<pre style="text-align: center;">/^[a-zA-Z0-9_\-\+]+(\.[a-zA-Z0-9_\-\+]+)+@[a-zA-Z0-9_\-\+]+(\.[a-zA-Z0-9_\-\+]+)+$/</pre>
<p style="text-align: left;">Evidentemente, no es perfecta ¿Qué tal si intentas mejorarla?</p>
<h2>Y mucho más</h2>
<p>La sintaxis de las expresiones regulares es mucho más rica de lo que acabo de exponer pero creo que al menos han quedado claras las bases para que comiences a investigar por tu cuenta.</p>
<p>Aún queda mucho por aprender: operador &#8220;|&#8221;, captura y repetición de grupos, grupos simbólicos (con nombre), captura hacia delante, captura non-greedy, etc.</p>
<p>En futuros artículos intentaré profundizar más en el tema e iré introduciendo expresiones regulares más complejas.</p>]]></content:encoded>
			<wfw:commentRss>http://ramblings.luismedel.com/2009/06/expresiones-regulares/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>
