<?xml version="1.0" encoding="utf8"?>
<!DOCTYPE html
	PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<HTML>
<HEAD>
   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=uft8">
   <META NAME="DATE" CONTENT="11/2/2003">
   <META NAME="Author" CONTENT="J. J. Merelo">
   <META NAME="Description" CONTENT="Tutorial de PERL en castellano, con ejemplos y ejercicios">
   <META NAME="Keywords" CONTENT="curso de perl, tutorial de perl, manual de perl, programacion"> 
   <TITLE>Tutorial de Perl:  Regularizando la situaci&oacute;n  </TITLE>
<script language="JavaScript">
var contador = 1;
var contadores= new Array;
function multicontador(  numero ) {
      if ( ! contadores[ numero ]  )
          contadores[ numero ] = 1;
      else
          contadores[numero ]++;
      document.write( contadores[numero] );
};

</script>
<link rel ='stylesheet' type = "text/css" href = "perl.css">
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#ffffff" LINK="#0000FF" VLINK="#551A8B" ALINK="#FF0000">
<TABLE  align=center cellpadding=5 bgcolor='darkseagreen'>
	<tr>

	<td>
			 <!-- Atomz.com Search HTML for Tutorial de PERL en castellano -->
	  <form method="get" action="http://search.atomz.com/search/">
	    <input size=25 name="sp-q"><br /><input type=submit value="Busca en el tutorial">
	    <input type=hidden name="sp-a" value="0002147b-sp00000001">
	  </form>
	</td>

	<td align='center'>
			<h1><a href='indice.html'>Tutorial de <strong>PERL</strong> en castellano </a>: Regularizando la situaci&oacute;n</h1>
	</td>

    <td align='right' valign='top'><a href='tutoperl9.html'> Pero ya puestos...</a><br><a href='tutoperl16.html'> Preguntas frecuentemente preguntadas</a><br><a href='tutoperl17.html'> Bibliograf&iacute;a.</a><br><a href='tutoperl18.html'> Recursos Internet</a><br><a href='tutoperl11.html'> Presentando lo impresentable</a><br></td>
	</tr>
</table>

<h3>Expresiones regulares: comparación y sustitución</h3>



<P>Una forma todav&iacute;a m&aacute;s directa de trabajar con estos ficheros
de texto que tienen una estructura regular (que son la mayor&iacute;a),
es usar <B>expresiones regulares.</B> Una expresi&oacute;n regular es una
forma de expresar gramaticalmente la estructura de cualquier cadena
alfanum&eacute;rica. 
Por ejemplo, una cadena compuesta por una letra inicial, con una letra
o un n&uacute;mero a continuaci&oacute;n se podr&iacute;a expresar de la
forma siguiente

<CENTER>letra {letra|n&uacute;mero}*</CENTER>
</p>

<P>donde<code> | </code>expresa una alternativa y <code>*</code>
indica que puede aparecer 0 o m&aacute;s veces. Pues bien, estas expresiones
regulares se utilizan enormemente en <strong>PERL</strong> (de hecho ya hemos usado una,
aunque compuesta por un solo carácter, en la l&iacute;nea 4 del programa
anterior), y cada vez que hemos usado <code>split</code>. Las
expresiones regulares se usan para hacer comparaciones, es decir,
hallar si un texto sigue o contiene una determinada expresión regular, 
y también para sustituir una subcadena que cumpla una expresión
regular por otra cadena.
</p>

<P>La expresi&oacute;n regular m&aacute;s simple es la propia cadena con
la que se quiere comparar; es decir, una cadena coincidir&aacute; consigo
misma. Las expresiones regulares en <strong>PERL</strong> siempre van
encerradas entre <code>//</code>, o bien  <code>m{}</code> (las llaves 
pueden ser sustituidas por cualquier otro elemento que no esté
incluido en la expresión regular), 
y si no se indica nada, comparar&aacute; la expresi&oacute;n regular con
la variable por defecto, <code>$_</code>, es decir, que
<code>/pepe/</code> ser&aacute; cierto si <code>$_</code>
contiene &iacute;ntegra la cadena <code>pepe</code>. El
programilla</p><pre>
$_ = "pepeillo"; print "si" if /pepe/;
</pre>
<P>imprimir&aacute;</p><pre>
si
</pre>

<P>Las expresiones regulares usan s&iacute;mbolos convencionales para referirse
a grupos de caracteres y otros para repetici&oacute;n o se&ntilde;ales
de puntuaci&oacute;n. En algunos casos, si el s&iacute;mbolo significa
algo dentro de la expresi&oacute;n regular (o en <strong>PERL</strong>), se precede por
<code>\</code> (escape). Por ejemplo, . significa "cualquier
car&aacute;cter" dentro de una expresi&oacute;n regular; luego para referirnos
al punto como tal, usaremos <code>\.</code>. Tambi&eacute;n se usa <code>\/,\?
</code>y <code>\*</code>, por ejemplo. Otras expresiones
m&aacute;s complicadas incluir&iacute;an repeticiones de s&iacute;mbolos;
por ejemplo, <code>\w+</code> casar&iacute;a con cualquier
palabra (un car&aacute;cter alfanum&eacute;rico repetido uno o m&aacute;s
veces).

<P><TABLE  width=100% cellpadding=2 cellspacing=0 border=0>
    <tr><td bgcolor=blue><table bgcolor=white cellspacing=0 cellpadding = 6><tr>
<td><table WIDTH="100%" NOSAVE >
<tr NOSAVE>
<td WIDTH="10%" NOSAVE>.</td>
<td>Describe cualquier carácter, excepto
newline.</td>
</tr>
<tr><td width="30%"> ( ) </td><td> Agrupa una serie
de patrones en un simple elemento.</td></tr>
<tr><td width="30%"> +,*,? </td><td> Coinciden con el elemento al
que preceden repetido 1 o más veces, 0 o más, ó 0 ó 1. Seguidos por
{N,M}, indican el número mínimo y máximo que debede aparecer; {N}
significa exactamente N veces; {N,}, como mínimo N veces.</td></tr>
<tr><td width="30%">  [..]  </td><td> Indica
una clase de caracteres, [^...] niega la clase, - indica un rango
decaracteres, como [a-z].</td></tr>
<tr><td width="30%"> (..¦..¦..)</td><td> coincide con una de las
alternativas.</td></tr>
<tr><td width="30%"> \w,\W </td><td>  coincide con los alfanuméricos, \W con los
no-alfanuméricos. </td></tr>
<tr><td width="30%">\s,\S </td><td> con los espacios en blanco, \S con los que no lo
son. </td></tr>
<tr><td width="30%"> \d \D </td><td>con los numéricos, \D no-numéricos.</td></tr>
<tr><td width="30%"> \b,\B </td><td> con límites de
palabra, \B con el interior de una palabra.</td></tr>
<tr><td width="30%"> $,^ </td><td> con el final de una
línea o cadena y con el principio.</td></tr>
</table>
</td></tr></table></td></tr><caption><font
face="arial,helvetica">Tabla 4: operadores de expresiones regulares</font></caption></table></p>

<p>Para agrupar s&iacute;mbolos se usa el par&eacute;ntesis,
que adem&aacute;s sirve para indicarle a <strong>PERL</strong> que con
lo que coincida con la expresión en su
interior se va a hacer algo. Para empezar, se asigna a la variable <code>$&amp;</code>;
pero adem&aacute;s, podemos asignar a otra variable el resultado de la
comparaci&oacute;n; en general, una comparaci&oacute;n con una expresi&oacute;n
regular que incluya par&eacute;ntesis devuelve una lista con todo lo que
coincida con el contenido de los par&eacute;ntesis
<pre><code>$zipi = "Pepeillo Gonzalez MacKenzie 8000";
@parejas = ($zipi =~ /(\D+) (\d+)/);</code>
</pre><P>(el
nombre de la variable y el s&iacute;mbolo <code>=~</code>
se pueden suprimir si la comparaci&oacute;n se hace sobre la variable por
defecto <code>$_</code>); <code>@parejas</code>
contendr&aacute; <code>("Pepeillo Gonzalez MacKenzie",8000),
</code>ya que la primera expresi&oacute;n regular indica
"uno o m&aacute;s caracteres no num&eacute;ricos", mientras que la segunda
representa "uno o m&aacute;s caracteres num&eacute;ricos".
</p>

<P>Por ejemplo, el fichero de paganinis usado en ejemplos anteriores anterior
tiene la estructura siguiente: una o m&aacute;s palabras, separadas por
un espacio, una cantidad (n&uacute;meros y puntos), una hora (n&uacute;meros
y dos puntos) y una fecha (n&uacute;meros y /). Esto se dice en <strong>PERL</strong> mediante
la expresi&oacute;n siguiente<code>(\D+) (\d+\.?\d+) (\S+)
(\d+)\/(\d+)\/(\d+)</code>que puede parecer un poco cr&iacute;ptica (y
en realidad lo es), pero 
cuyo significado se puede resolver mirando la tabla
4. Con esta modificaci&oacute;n, el bucle central del programa <a href=totales.pl>totales.pl</a>  se queda reducido a<pre><code>
while(<>) {
    ($paganini, $pasta, $hora, $dia, $mes) = /(\D+) (\d+\.?\d+) (\S+) (\d+)\/(\d+)\/(\d+)/;
    $totalDia{"$mes-$dia"}+=$pasta;
}</code></pre>
</p>

<P>M&aacute;s compacidad no se puede pedir. Adem&aacute;s, ya de camino,
nos vamos ahorrando algunas variables. En la primera l&iacute;nea del interior
del bucle se asigna la parte de la cadena que coincide con lo descrito
en el interior de los par&eacute;ntesis a cada una de las variables, es
decir, divide la l&iacute;nea en cinco campos, cada uno de los cuales tiene
un tipo diferente. Hay cinco pares de par&eacute;ntesis, para cinco variables.
Veamos cada expresi&oacute;n regular por partes.
</p>

<TABLE width=200 cellpadding=2 cellspacing=0 border=0 align=right>
    <tr><td bgcolor=black>
    <TABLE width=100% cellpadding=4 cellspacing=0 border=0>
<tr><td bgcolor=white>
Las expresiones regulares se usan en casi todos los lenguajes de programación; el ligro <a
href=http://www.amazon.com/exec/obidos/ASIN/1565922573/perltutobyjjmere><em>Mastering
Regular Expressions: Powerful Techniques for Perl and Other Tools
</em>, de  Jeffrey E. Friedl (Editor), Andy Oram (Editor)</a>, el
libro del búho. Es un libro avanzado, pero útil para quien quiera
descifrar el arcano arte de las expresiones regulares<br><br>
<a
href=http://www.amazon.com/exec/obidos/ASIN/1565922573/perltutobyjjmere>
<center><img
src='http://images.amazon.com/images/P/1565922573.01.LZZZZZZZ.jpg' 
  alt="[Mastering regular expressions front page]" 
	align=center border=0 width='200'></center></a>
</td></tr></table></td></tr></table>

<P>La primera expresi&oacute;n regular es <code>\D+</code>.
Como se ve en la tabla 4, <code>\D</code>
coincide con todo lo que no sea num&eacute;rico, y en particular letras
y espacios. Este campo es problem&aacute;tico, porque puede incluir una
o varias palabras; sin embargo, sabemos que el siguiente campo comienza
por un n&uacute;mero; por tanto, incluiremos en este campo todo lo que
haya hasta que encontremos un n&uacute;mero. Se puede insertar el c&oacute;digo
siguiente <code>print $paganini;</code> en el bucle para ver qu&eacute;
se incluye dentro de ese campo.
</p>

<P>La siguiente expresi&oacute;n regular,<code> (\d+\.?\d+)</CODE>,
no describe otra cosa que un n&uacute;mero real en notaci&oacute;n de coma
flotante, es decir, una o m&aacute;s cifras <code>(\d+)</CODE>,
seguidas o no por un punto (<code>\.?</CODE>, no olvidar
que el punto tiene significado especial en las expresiones regulares),
que a su vez debe de estar seguido por una o m&aacute;s cifras <code>(\d+)</CODE>.
Esta expresi&oacute;n regular coincide adem&aacute;s con aquellos n&uacute;meros
sin punto enmedio.
</p>

<P>Y la siguiente <code>\S+</CODE> coincide con la hora,
aunque quiz&aacute;s podr&iacute;a coincidir con cualquier cosa, simplemente
coge todo lo que haya entre los dos espacios. En realidad, si pusi&eacute;ramos
toda la expresi&oacute;n regular anterior como<code>(\S+) (\S+) (\S+)
(\S+)</CODE> funcionar&iacute;a perfectamente.
</p>

<P>Y para terminar,<code>(\d+)\/(\d+)\/(\d+)</CODE>coincide con
el d&iacute;a, el mes y el año, que est&aacute;n separados por una barra diagonal.
</p>

<P>Con estas expresiones regulares se pueden construir programas superpotentes,
que convierten casi cualquier texto en casi cualquier otro. Incluso, si
uno se atreve (nuestro pol&iacute;tico corrupto no creemos que se atreva)
un analizador sint&aacute;ctico de un lenguaje de alto nivel, aunque sea
simplificado. Otra utilidad es hacer filtros de correo electr&oacute;nico,
o de noticias de USENET (de hecho, el <I>killfile</I> o fichero que incluye
todas las expresiones regulares de mensajes que deben de ser borrados,
usa expresiones regulares). Tambi&eacute;n se pueden usar expresiones regulares
en archie y en la Web; el problema es que la mayor&iacute;a de las veces
usan convenciones diferentes, pero en cualquier caso, nunca viene mal saberlas.
</p>

<P>El pol&iacute;tico corrupto decide no contarle al se&ntilde;or X todo
el dinero obtenido (por no mencionar a Hacienda), y poni&eacute;ndose a
trabajar sobre los ficheros generados anteriormente (como <code>cliente.mas</CODE>),
elabora el siguiente programilla (<a href=changec.pl><code>changec.pl</CODE></a>)<pre><code>
#!/usr/local/bin/perl -p
s/(\d+\.?\d+)/$&amp;*0.85/e;</code></pre>
<P>que ni siquiera pongo en un cuadro aparte, porque no merece la pena.
</p>

<P>En este programa se introducen novedades desde la primera l&iacute;nea.
Para empezar, se usa <strong>PERL</strong> con un switch en la
l&iacute;nea de comandos, 
<code>-p</CODE>. Este switch indica que alredededor de lo
que hay debajo hay que sobreentender el siguiente bucle<pre>
<B>while(&lt;>)
{</B>
    s/(\d+\.?\d+)/$&amp;*0.85/e;
    <B>print;
}</B></pre>

<P><TABLE align=right width=50% cellpadding=2 cellspacing=0 border=0>
    <tr><td bgcolor=blue><table bgcolor=white cellspacing=0 cellpadding = 6><tr>
<td><table WIDTH="100%" NOSAVE >
<tr NOSAVE>
<td WIDTH="10%" NOSAVE>
-n	</td><td>Incluye el bucle while(<>) {}alrededor de las órdenes del fichero.</tr>
<tr><td WIDTH="10%" NOSAVE>-p	</td><td>	Incluye el bucle
while(<>) {print;} alrededor de las órdenes del fichero.</tr>
<tr><td WIDTH="10%" NOSAVE>
-a	</td><td>Modo autosplit, es decir, que al principio del bucle anterior incluye la orden
split.</tr>
<tr><td WIDTH="10%" NOSAVE>
-d	</td><td>Modo de depuración. En vez de ejecutar el programa, se ejecuta el depurador
sobre el programa.</tr>
<tr><td WIDTH="10%" NOSAVE>
-i[extensión]</td><td>
	En combinación con -p y -n, en vez de imprimir en salida estándar, trabaja sobre
el mismo fichero, creando una copia de
seguridad con la extensión que se indica,
</td></tr>
</table>
</td></tr></table></td></tr><caption><font
face="arial,helvetica">Tabla 5: Switches o interruptores que se pueden 
usar en la línea de comandos</font></caption></table></p>

<p>; es decir, un bucle que abre y lee del fichero que se pasa en la l&iacute;nea
de comandos, ejecuta las &oacute;rdenes encontradas en el fichero, y al
final imprime la variable por defecto. Si el switch fuera <code>-n</code>
en vez de <code>-p</code> no imprimir&iacute;a.
</p>



<P>Tambi&eacute;n se incluye la nueva orden <code>s///[switches]</code>tomada,
como otras &oacute;rdenes, del editor de UNIX <code>sed</CODE>.
En concreto, esta orden lo que hace es sustituir la expresi&oacute;n regular
encontrada entre los primeros <code>//</code> por la expresi&oacute;n
en los segundos. En este caso act&uacute;a sobre la variable por defecto,
pero en general<code>$zipi="pepeillo";
$zipi=~ s/p/q/g;</code>dar&iacute;a "qeqeillo".
El switch <code>e</CODE> que aparece al final de la orden
en el programa indica que se tiene que evaluar la expresi&oacute;n que
aparece en la segunda parte de la orden; el <code>g</CODE>
que aparece aqu&iacute; indica que se tiene que hacer una sustituci&oacute;n
<I>global</I>, es decir, que tienen que sustituirlo todas las veces que
aparezca en la primera expresi&oacute;n, no solo una vez.
</p>


<P>Por &uacute;ltimo, la variable <code>$&amp;</CODE> contiene
la cadena que coincida con la expresi&oacute;n regular en la primera parte
de la orden; los par&eacute;ntesis indican qu&eacute; parte de la expresi&oacute;n
hay que asignar a tal variable. Si hay varios par&eacute;ntesis, las cadenas
correspondientes ser&aacute;n asignadas a las "variables" <code>\1,
\2</CODE> (dentro de la orden <code>s</CODE>), o <code>$1,
$2...</CODE> fuera de la expresi&oacute;n (estas variables s&oacute;lo
funcionan un ratito, asi que no van a estar disponibles hasta que uno quiera;
en caso de duda, es mejor asignarlas inmediatamente a otra variable.
</p>

<p>Toda la información sobre expresiones regulares está en la página
de manual <code>perldoc perlre</code>.

<p><table class=ejercicio width=100% cellpadding=2 cellspacing=0 border=0>
    <tr><td bgcolor=green><table bgcolor=white cellspacing=0 cellpadding = 6><tr>
<td><ol>
<li> Dado un fichero que contenga varias líneas en el formato siguiente:<pre>
Apellido 1 Apellido 2, Nombre		Nota como un número real</pre>
poner los nombres en el formato Nombre Apell1 Apell2 Nota como Calificación
(Notable, Sobresaliente, etc), ordenándolos por orden de nota.
</li>
<li>Dado un fichero que contenga URLs, o direcciones internet, generar otro que
incluya alrededor de las mismas una referencia del tipo &lt;a href=URL&gt;URL&lt/a&gt;.</li>
<li>Hacer un programa que imprima todos los ficheros de cabecera incluidos en un
programa en código fuente C. Estos tienen el formato #include &lt;nombre-de-
fichero.h&gt;. Comprobarlo sobre algún fichero el directorio
/usr/include (en UNIX),  o cualquier otro que pille a mano.</li>
<li> En un programa en código fuente C o PERL, imprimir las líneas en las que
comience un bucle for, e imprimir la variable de bucle que se usa.</li>
<li> En un programa en código fuente C, sustituir todas las constantes definida usando
la directiva de precompilación #define por su valor, definido en la misma orden. </li></ol>

</td></tr></table></td></tr><caption><font face="arial,helvetica">Ejercicios <script>multicontador(3);</script></font></caption></table>



<TABLE  align=center cellpadding=5 bgcolor='darkseagreen'><tr><td>[<a href='tutoperl9.html'> Pero ya puestos...</a>] [<a href='tutoperl16.html'> Preguntas frecuentemente preguntadas</a>] [<a href='tutoperl17.html'> Bibliograf&iacute;a.</a>] [<a href='tutoperl18.html'> Recursos Internet</a>] [<a href='tutoperl11.html'> Presentando lo impresentable</a>] </td></tr></table>

</BODY>
</HTML>
