1- Introducción
1.1- PERL
1.1.1- Que es PERL
Perl (Practical Extraction and Report Languaje) es un lenguaje de
programación surgido a inicios de los noventas, que busca antes que nada el
facilitar la elaboración de tareas comunes en sistemas tipo UNIX, donde
tradicionalmente las tareas de administración y proceso de datos se realiza
con herramientas muy rudimentarias y por demás hostiles al usuario o
administrador. Pero que se aplican sobre grandes cantidades de información
(por lo regular texto) por lo que se requiere que sean de alto rendimiento.
Su autor, Larry Wall ([email protected]) realizó Perl casi como una obra
altruista, de modo que hasta PERL 5.X su distribución es gratuita, sin que
por eso tenga menos poder o consistencia.
1.1.2- Para que sirve
Perl surgió como una opción para una gran cantidad de herramientas de UNIX
en las cuales basa su propia sintaxis, buscando el mínimo sacrificio de su
desempeño por una máxima facilidad de programación e integración, sigue la
filosofía de mantener un ambiente que sea capaz de detectar y corregir pequeñas
omisiones del programador, y de proporcionarle una forma abreviada de realizar múltiples
tareas. En una palabra, es una utilería que pretende facilitar el proceso de
grandes volúmenes de información sin sacrificar el rendimiento.
1.1.3- Donde Puede Usarse
Las plataformas donde Perl se ha desarrollado mas son los servidores UNIX,
por sus necesidades de administración y lo robusto de su manejo de memoria y de
procesos (requisitos de PERL hacia el S.O.) además de la facilidad de Perl para
realizar los así llamados CGIs, interfaces para comunicar recursos del servidor
con un servicio de internet particular (como podría ser WWW o gopher), En otras
plataformas, PC en particular, se han desarrollado versiones que mantienen un
razonable grado de funcionalidad, pero en realidad, el sistema DOS no tiene un
manejo lo bastante bueno de los procesos o de la memoria para permitir a PERL
dar un buen desempeño, además de que no es común ver en PC necesidades de
administración de la magnitud de un servidor institucional. Sin embargo, puede
practicarse la programación en PERL de PC, o incluso elaborar programas de
reporteo en el , sin embargo, es algo que no se ha popularizado hasta hoy.
1.1.4- Que fuentes de información existen
Los libros que son ya clásicos sobre Perl son los libros del camello y la
llama de la editorial Nutshell, además, existen magníficas introducciones y
manuales de referencia que se pueden obtener vía internet. Aun cuando es
imposible mencionar con precisión las fuentes de información de un medio tan
dinámico con algo tan estático como este documento. Debe notarse, además que
estas referencias están en inglés.
Para buscar información, Yahoo! por supuesto:
http://www.yahoo.com/Computers_and_Internet/Languajes/Perl/
Fuente de información general y referencia de documentos:
http://pubweb.nexor.co.uk/public/perl/perl.html
http://www.khoros.unm.edu:80/staff/neilb/perl/perl5.html
Una buena formada introducción:
http://www.khoros.unm.edu:80/staff/neilb/perl/introduction/home.html
Documentación:
http://www-cgi.cs.cmu.edu/cgi-bin/perl-man
http://www.perl.com/perl/info/documentation.html
ftp://ftp.cdrom.com/pub/perl/CPAN/doc/manual/html/index.html
ftp://ftp.uoknor.edu/mirrors/CPAN/doc/manual/html/index.html
Debo recalcar que por la misma naturaleza de Perl, los recursos disponibles y
las herramientas que se pueden utilizar cambian muy a menudo, por lo que es
indispensable dedicar algún esfuerzo a mantenerse al día para evitar un
desperdicio mayor de esfuerzo por no utilizar los nuevos recursos disponibles.
1.2- Filosofía de Perl
"Hay mas de una forma de hacerlo"
-Larry Wall, autor del lenguaje de programación Perl.
Perl no establece ninguna filosofía de programación (de hecho, no se puede
decir que sea orientado a objetos, modular o estructurado aun cuando soporta
directamente todos estos paradigmas), los objetivos que se tuvieron en cuenta al
diseñar la sintaxis de Perl fueron la facilidad de aprendizaje y de uso y la
claridad de código, las cuales, considero que son necesarias (aunque pueden
escribirse programas en Perl complejos e inteligibles si así se desea).
Por si fuese poco, Perl no es ni un compilador ni un interprete, esta en un
punto intermedio, cuando mandamos a ejecutar un programa en Perl, se compila el
código fuente a un código intermedio en memoria, se le optimiza (como si fuésemos
a elaborar un programa ejecutable) pero es ejecutado por un motor, como si se
tratase de un interprete. El resultado final, es que utilizamos algo que se
comporta como un interprete pero que tiene un rendimiento comparativo al de
programas compilados. Sin embargo, ya existen compiladores de Perl con la versión
5.
En fin, Perl no nos forza a nada, pero como es lógico hay ciertas reglas que
recomiendo seguir para facilitar nuestro trabajo:
-
Claridad. En la mecánica de programación actual, los programas
deben de ser entendibles por la persona que nos suceda en tareas de
mantenimiento, de lo contrario perjudicamos tanto a nuestros compañeros de
trabajo como a nuestra propia libertad para progresar y mantenernos libres
de preocupaciones.
-
Indentación. Una costumbre ya clásica de la programación, en lo
personal, y a lo largo de los ejemplos de este documento, indento el código
dos espacios hacia adelante al abrir cada bloque, y termino la indentación
al terminar el bloque, de modo que las llaves de apertura y cierre quedan a
la vista y en la misma columna, solas en sus renglones (esto incrementa algo
el numero de líneas, pero facilita sobremanera la búsqueda y corrección
de los diversos bloques de control).
-
Nombres de variables y demás. En lo personal, procuro dar la máxima
claridad a los nombres de las variables sin hacerlos demasiado grandes, el
nombre de los contadores y variables que guardan valores concernientes a un
pequeño segmento de código por lo regular son de un par de letras (c1, c2,
... cx para los contadores, s1, s2, etc para cadenas de entrada etc.)
mientras que las variables que afectan a diversos segmentos (a modo de
regla, que tienen su definición en una pantalla distinta a donde se usan)
tienen nombres explicativos que procuro no excedan los 12 caracteres. Además,
los nombres de archivos se usan con mayúsculas (ARCHENT, ARCHSAL, etc) y
las clases tienen su primera letra mayúscula.
-
Comentarios. Para facilitar la comprensión de un programa no hay
como explicarlo, y los comentarios son el medio ideal para hacerlo, hay por
lo menos tres comentarios que considero que siempre deben incluirse en un
programa: Que hace el programa, Quien lo escribió y Cuando inicio y termino
de escribirlo, sobretodo en el contexto de una organización, estos tres
simples comentarios pueden hacer la diferencia entre desechar un programa
como indescifrable o dedicarle algún tiempo para revisarlo. Además,
considero prudente comentar dentro del código la forma en que el programa
deberá ejecutarse, parámetros, y su sintaxis, así como comentar las
estructuras de control como un modo de explicar la funcionalidad al detalle
y recalcar con comentarios las funciones que cumplen las variables.
-
Sencillez. Es cómodo en ocasiones el comprimir una serie de
instrucciones en una sola línea, queda al criterio decidir cuando se gana
en claridad con un código mas o menos extenso, pero no debe titubearse en
comentar el código que sea "comprimido".
1.3- Diferencias entre Perl 4.3 y 5.X, Como elegir versión
Actualmente existen dos versiones altamente populares de Perl, la 4.3 y la
5.0X, de hecho hay diferencias importantes entre una versión y otra.
Seguramente el lector se pregunta porque surge la duda entre usar una versión
vieja y una nueva, por regla general las nuevas versiones son mejores que las
anteriores de modo que las opacan en todo sentido, Perl no es la excepción a
esta regla, el único factor que impide una transición inmediata es que no son
100% compatibles. La versión 5 de Perl es una reescritura total que ya incluye
un manejo de estructuras abstractas de datos mucho mas poderoso, incluso,
soporta la orientación a objetos a su manera (tema que no trato en esta
introducción). De modo que las librerías, por ejemplo para creación de CGIs
no funcionan de una función a otra por lo que la migración es poco practica.
Así pues, la decisión sobre que versión utilizar depende del trabajo que
haya sido realizado con anterioridad, si ya se tiene un sistema completo o
grande en Perl 4, es recomendable mantenerlo en Perl 4 por el resto de su vida
útil, pero para desarrollos nuevos es por mucho, mas recomendable iniciarlos
con Perl 5 o en su caso, la versión mas reciente que este disponible, por
experiencia se que la capacitación para adaptarse a la nueva versión es
extraordinariamente corta dada la importancia de las mejoras. Una tercera opción
(que es por mucho la mas recomendable si se tienen los recursos) consiste en
instalar las dos versiones de modo que convivan en el sistema.
2- Programación básica
En este capitulo daremos una rápida revisión a los conceptos mas usuales
que se encuentran en un programa en Perl, trataremos de ver la implementación
para ambas versiones 4 y 5 cuando sea posible, especificando siempre en que
versión esta el ejemplo original (dando preferencia a la versión 5) y al menos
las alternativas para implementarlo en la otra versión.
Los ejemplo requerirán para funcionar, que se tenga Perl instalado en la
maquina en que se practique y deberá conocerse la ruta completa al binario de
Perl.
Mas que sentirse en libertad de experimentar con los diversos ejemplos, deben
sentirse obligados a experimentar, modificar y explorar por su cuenta cada que
sea posible, tomando los ejemplo solo como un punto seguro de partida. Para este
fin, recomiendo que se tenga acceso desde el inicio a alguna referencia del
lenguaje (ya sea el libro Programming Perl (del Camello) de la Nutshell o alguna
referencia disponible por WWW), este documento pretende explicar lo básico de
un modo accesible para que después pueda el nuevo programador de Perl abordar
sin temor los materiales mas detallados.
2.1- Estructura Básica de un programa, programa Holamundo.
Como se menciona en la introducción, Perl no obliga a casi nada, así pues,
lo que planteo como estructura básica es mas bien una convención que un
requisito del lenguaje, a diferencia de Pascal (por ejemplo) Perl no tiene una
plantilla para sus programas y si se adoptan algunos protocolos es solo por
comodidad.
Los programas de Perl, por lo regular, inician con la línea:
#!/usr/bin/perl
Esta línea, indica al SO que lo que sigue es un script de Perl, y que "Perl"
(el programa con el cual debe ejecutarse) esta en el directorio "/usr/bin",
la secuencia "#!" es una secuencia que UNIX reconoce, no Perl.
Un método alterno, que funciona para otras plataformas, es: en lugar de
colocar esa primera línea ejecutamos:
Perl nombre_del_script.pl
de modo que directamente se ejecuta el interprete de Perl pasándole como
primer parámetro el script a ejecutar (los demás parámetros se tomaran como
parámetros al programa). Si se requiere deberá sustituirse "Perl"
por la ruta completa del programa y el nombre que el programa tenga.
Para los ejemplos sucesivos, tomare la suposición de que se trabaja en un
sistema UNIX con el interprete de Perl en el directorio "/usr/bin".
Y eso es toda la estructura básica!.
Programa Hola mundo:
#!/usr/bin/perl
print "Hola Mundo\n"; #Saluda
#Programa Hola Mundo, Daniel Sol Llaven, 1996, como parte del tutorial de Perl
Este programa, se escribe como un archivo de texto común, (al que recomiendo
se le ponga extensión ".pl") y se cambian sus permisos para que pueda
ser ejecutado (por lo regular con un "chmod 700 nombre_programa.pl" en
sistemas UNIX), para ejecutarlo simplemente se invoca el nuevo script "nombre_programa.pl",
hay que recordar que para el sistema particular en que se este trabajando es muy
probable que deba modificarse "/usr/bin/" por otra ruta.
Así, la ejecución en un sistema UNIX podría verse como:
>chmod 700 Hola.pl
>Hola.pl
Hola Mundo
>Hola.pl
Hola Mundo
>
Ejecutando dos veces el script pretendo mostrar que no es necesario cambiar
el modo de ejecución del script sino solo una vez.
Expliquemos las tres líneas que constituyen nuestro primer script:
#!/usr/bin/perl
Esta línea, propiamente dicho, no es parte del script de Perl, sino una
instrucción al SO para indicar que las siguientes líneas deberán
interpretarse por el programa "/usr/bin/Perl", si se omite, o no
funciona en el sistema operativo, podemos ejecutar el programa de la siguiente
forma:
>/usr/bin/Perl Hola.pl
Hola Mundo
>print "Hola Mundo\n"; #Saluda
Esta es la única instrucción del programa y esta acompañada por un
comentario, la instrucción es:
print "Hola Mundo\n";
que pudo ser escrita como:
print("Hola Mundo\n");
sin que esto implicara el mínimo cambio en funcionalidad.
a continuación tiene el comentario:
#Saluda
La sintaxis de C para comentarios no funciona en Perl, pues se usa a
"/" para expresiones regulares.
#Programa Hola Mundo, Daniel Sol Llaven, 1996, como parte del tutorial de Perl
Esta línea es otro comentario, el SO y Perl consideran todo lo que este en
una línea que inicie con "#" como comentario. (excepto en el caso de
que sea una primera línea, el SO lo interpreta como la indicación del
interprete, pero Perl lo ignora como a cualquier otro comentario).
2.2- Estructuras de Datos Básicas
En nuestro programa Hola.pl no utilizamos ninguna variable, por lo que para
explicar estos conceptos necesitaremos un nuevo programa, HolaP.pl que es una
versión personalizada de Hola.pl.
#!/usr/bin/perl
#Programa Hola Mundo personalizado, Daniel Sol Llaven 1996, Tutorial Perl.
print "Hola ?como te llamas?:";
$nombre=<STDIN>;
chop($nombre);
print "$nombre!, bonito nombre, cuantos años tienes?:";
$edad=<STDIN>;
print "sabias que te faltan ".(100-$edad)." para tener 100?\nAdios!\n";
Su ejecución genera resultados similares a los siguientes:
>HolaP.pl
Hola ?como te llamas?:Daniel
Daniel!, bonito nombre, cuantos años tienes?:22
sabias que te faltan 78 para tener 100?
Adiós!
>
En este programa, aparecen muchos elementos novedosos que iremos revisando
poco a poco a lo largo de este documento, por el momento, revisemos que hace línea
por línea:
#!/usr/bin/perl
#Programa Hola Mundo personalizado, Daniel Sol Llaven 1996, Tutorial Perl.
Cabecera normal del programa, incluido comentario indispensable.
print "Hola ?como te llamas?:";
Esta línea escribe "Hola ?como te llamas?:" pero nótese que no
escribe una vuelta de carro al final (omite el "\n").
$nombre=<STDIN>;
Aquí asignamos a $nombre un renglón escrito por la entrada
estándar del programa.
Perl define al "archivo" STDIN como su entrada estándar,
y el operador <> que indica la lectura de una línea de un
archivo de texto, de modo que <STDIN> indica la lectura de
un renglón de la entrada estándar (al menos para el ejemplo!), este tema se
tratara con cuidado en la sección 2.5 y 3.3 de este documento que tratan de
operaciones con archivos.
Para fines prácticos, usaremos <STDIN> como la expresión
que lee una línea de entrada del usuario desde teclado.
chop($nombre);
Perl, al leer la línea escrita por el usuario, también toma el enter que el
usuario da, de modo que en el ejemplo, la entrada que di en vez de ser "Daniel"
se puede escribir como "Daniel\n", (siendo "\n"
el carácter de vuelta de carro), la función "chop()"
tiene la función de eliminar el ultimo carácter a nuestra cadena, de modo que
después de esta instrucción $nombre solo tiene "Daniel".
Por las características de la lectura de renglones de archivos, chop()
es un elemento casi constante en programas de Perl.
(Pruébalo!, revisa que sucede si comentas esta línea, veras que al imprimir
el nombre imprime también el retorno de carro avanzando una línea hacia
abajo).
print "$nombre!, bonito nombre, cuantos años tienes?:";
Esta línea nos muestra como se imprime el contenido de nuestras variables de
forma sencilla; notamos que en la cadena a imprimir se encuentra $nombre,
el compilador lo interpreta y reemplaza por su valor, (para imprimir
"$" se pone "\$"), el resto de la cadena es
completamente convencional.
$edad=<STDIN>;
Esta línea, lee otra línea de TEXTO y la coloca en la variable $edad
(con todo y retorno de carro).
print "sabias que te faltan ".(100-$edad)." para tener 100?\nAdios!\n";
Esta línea imprime un mensaje que esta dividido en tres partes, la primera y
la ultima son cadenas convencionales, que están concatenadas (operador .) con
una operación aritmética. Hay que notar que edad era un renglón de la entrada
estándar, y no un numero, sin embargo no hay conflicto de tipo al operarlo con
un entero, ¿porque? porque los dos, para Perl, son del mismo tipo, son
escalares, después discutiremos que son los escalares y que mecanismo de
conversión se utiliza para extraer el entero del texto contenido en $edad.
(Experimenta!, prueba que pasa si los valores que se dan a edad no son
enteros, que pasa si son reales o cadenas!)
Después de nuestro primer encuentro con las variables de Perl, el
sentimiento mas probable es el de perplejidad, ¿como maneja sus variables?, ¿a
que me refería conque enteros, reales y cadenas son del mismo tipo?, y si es así,
¿como serán los demás tipos?.
En realidad, las variables de Perl pretenden simplificarnos la vida, pero
debemos comprender como pretenden facilitar las cosas para no terminar peleando
contra ellas.
2.2.1- Clases de Datos
Perl reconoce tres clasificaciones básicas de datos, y dos especiales, por
claridad, llamaremos clases a estas diversas clasificaciones, y tipos a las
formas usuales de datos.
Las diversas clases se distinguen entre si por el símbolo que antecede al
nombre de la variable (por ejemplo $nombre es una variable de tipo escalar que
se llama "nombre"), debe notarse que no hay relación entre variables
del mismo nombre si son de clases distintas.
A continuación, pongo una tabla de las clases de datos y los tipos que
contienen:
Clase Símbolo Tipos
Escalar $ Entero, Real, Cadena, Referencia*
Arreglo @ Arreglo de escalares
Hash % Arreglo Asociativo de escalares
Archivo (ninguno) Identificador de Archivo
Type Glob * Cualquiera**
* Las referencias son exclusivas de Perl 5, son el equivalente a apuntadores.
** Las variables tipo Glob se usaban como sustituto de las referencias en
Perl 4, son obsoletas para Perl 5 y en lo personal no recomiendo su uso sino en
casos muy particulares.
2.2.1.1- Escalares
Nota: Algunas características de las conversiones entre tipos son exclusivas
de Perl 5, pero la sintaxis y características generales son las mismas para
ambas versiones. Por lo que en general, el tratamiento expuesto es valido para
las dos.
El escalar, es la clase de datos que engloba los tipos convencionales de
datos, de modo que podemos decir:
$v1="Daniel Sol Llaven";
$v1=100;
$v1=89.12;
Sin que esto implique ningún cambio en la naturaleza de $v1, en todo momento
es un escalar.
Aun cuando la compatibilidad de datos enteros y reales es fácil de imaginar,
la conversión implícita en el caso de las cadenas no lo es, sin embargo la
regla es bastante simple.
por ejemplo:
$v1="123y321";
crea un escalar $v1, que contiene la cadena "123y312",
¿pero que pasa si le deseamos sumar 4?. Perl, interpreta $v1
como entero (o punto flotante) para esto, toma todos los caracteres del inicio
que forman un numero correcto y ese numero es el que interpreta; de modo que:
print $v1+1;
imprimirá:
124
Del mismo modo, como ejemplo:
$v2="123.123.123"+1
da el valor 124.123 a la variable $v1 (punto flotante).
Otro punto importante de las variables en general es que, aunque en ningún
momento se declaran como de un tipo o de otro, si se pueden
"destruir", o revisar que hayan recibido algún valor en la ejecución
del programa, esto se logra mediante las funciones defined() y undef().
defined nos indica si la variable que le pasamos como argumento ha sido
definida (es decir, existe en el programa) o no. undef toma una variable
y la "elimina" de modo que ya no tiene valor y defined
la reporta como no utilizada.
Los valores lógicos de verdadero y falso se manejan de modo similar al de C,
cualquier valor escalar no nulo o cero se considera verdadero. Debe tenerse
cuidado con las cadenas "0" pues si se evalúan como número
resultaran en falso aun cuando no son cadenas nulas.
Los escalares son los constituyentes de las demás estructuras de datos, por
lo que al explicar los arreglos, hashes, referencias y archivos haremos muchas
referencias a ellos.
2.2.1.2- Arreglos
Los arreglos en Perl son simplemente, arreglos dinámicos de escalares, es
decir, se pueden usar cualesquiera elementos en el arreglo y Perl se encargará
de hacer al arreglo de la dimensión adecuada.
La definición de un arreglo con valores constantes es:
@a1=("hola",123,43.2,"adios");
Esta definición, crea el arreglo @a1 con cuatro elementos, dos cadenas, un
entero y un real, en realidad, cuatro escalares, para hacer referencia a los
elementos escalares de un arreglo se usan los corchetes [] indicando el índice
del elemento (de cero al numero de elementos menos uno) de modo que:
print "$a1[0]\n$a1[1]\n$a1[2]\n$a3[3]\n";
resulta para el arreglo anterior:
hola
123
43.2
adiós
Nótese que el elemento de un arreglo ya no es un arreglo, sino un escalar, y
debe especificarse como tal, con $ en vez de @. No es lo mismo el escalar a1
que un elemento del arreglo a1.
$a1 Es un elemento distinto que $a1[]
Por claridad, recomiendo que no se dupliquen nombres de arreglos (o cualquier
otra cosa) con escalares, aunque Perl da un manejo independiente a las diversas
entidades, de modo que si se hace no habrá quejas de parte de Perl.
Es importante señalar que los arreglos y hashes no pueden ser elementos de
un arreglo, si se intenta esto, los arreglos o hashes serán
"aplanados" a elementos que se agregan al arreglo. Por ejemplo:
(a,b,(c,d),e)==(a,b,c,d,e)
Es decir, el arreglo constante a,b, arregló con c,d , e es equivalente al
arreglo que contiene a,b,c,d,e. Pues al formar el primer arreglo, el sub-arreglo
c,d es "aplanado" a elementos que se agregan al arreglo principal.
Algo similar sucede a los hashes.
Para hacer arreglos de arreglos o arreglos como elementos de hashes, se
utilizan las referencias.
2.2.1.3- Hash o Arreglos Asociativos
El Hash, o arreglo asociativo es una ampliación del arreglo, en la que en
vez de ubicar los elementos por posición se les ubica por una "llave"
es pues un arreglo de parejas ordenadas que se accesa por el primer elemento, el
símbolo de los hashes es %, y la forma de declarar un hash con constantes es
muy similar a la forma para declarar un arreglo:
%h1=("ll1",1,"ll2",2,"ll3",3);
Esta declaración crea un hash con tres parejas ordenadas, las llaves son "ll1",
"ll2", "ll3", para usarlo se
pueden usar expresiones del tipo:
$ndx="ll3";
print "$h1{ll1}\n$h1{"ll2"}\n$h1{$ndx}\n";
resultando:
1
2
3
Al igual que en los arreglos, cada elemento de un hash es un escalar, de modo
que debe anteponerse $ en vez de % (pues no estamos haciendo referencia a todo
el hash, sino a un elemento escalar del hash), pero a diferencia del los
arreglos, en vez de usar [] para indicar el índice se usan las llaves {}.
Dentro de las llaves, en el ejemplo, usamos las tres formas principales de
dar el índice:
ll1 - En esta forma Perl adivina correctamente que ll1 es una
cadena y que esta es la llave.
"ll2" - En esta forma decimos explícitamente el
valor de la llave deseada.
$ndx- como $ndx="ll3" Perl puede
determinar el valor de la llave correcta.
Para índices, al igual que para hashes, también puede usarse el valor de
variables o de expresiones para especificar el índice.
2.2.1.4- Equivalencias de Clases
Como ya revisamos, al hacer referencia a un elemento particular de un arreglo
o hash obtenemos un escalar (en ves de todo el arreglo o hash). Este tipo de
"cambios" de clase son el propósito de esta sección, pues pueden
resultar algo confusos aunque, una vez comprendidos, dan una buena parte del
sabor peculiar de Perl.
Básicamente, Perl distingue dos tipos de contextos para evaluar una expresión:
como escalar y como arreglo. El primero se refiere a las expresiones que han de
regresar un escalar (del tipo que sea) como resultado, y el segundo a aquellas
expresiones que han de regresar un arreglo o conjunto de escalares como
resultado. Muchas expresiones pueden ser evaluadas en ambos contextos y obtener,
según el contexto, un resultado distinto, esto lo revisaremos con cuidado
conforme vayamos revisando estas expresiones.
Revisemos los cambios que ocurren a los datos de una cierta clase cuando los
evaluamos en otro contexto:
Contexto escalar arreglo
Clase escalar arreglo hash
escalar escalar El valor Se convierte en Vacío, no definido
original el único
elemento del
arreglo
arreglo arreglo Numero de El arreglo de Los elementos pares
elementos valores (0,2,4,...) forman
(usualmente) originales las llaves y los
nones los datos del
nuevo hash.
hash cociente que arreglo con las El hash original
representa la parejas
eficiencia del llave1,valor1,lla
hash ve2,valor2,...
Las transiciones mas utilizadas son las de arreglo a escalar, las de arreglo
a hash y de hash a arreglo, la primera porque permite conocer el tamaño de los
arreglos y las segundas porque proveen los mecanismos para inicializar los
hashes con arreglos constantes y para "aplanarlos" con fines de
almacenamiento e impresión.
A menudo, se representa un arreglo con una sola cadena que contiene
separadores para los diversos elementos, Perl implementa la función "split"
que divide estas cadenas en arreglos, su sintaxis básica es:
split($delim,$cadena)
donde
$delim es la expresión que representa los delimitadores de
elementos y
$cadena es la cadena a dividir.
Como ya se mencionó, generará un arreglo de cadenas donde los elementos son
las subcadenas de $cadena que estén separadas por cadenas $delim.
por ejemplo, una cadena como:
$registro="Daniel:22:daniel@simba";
@reg=split(":",$registro);
print "Nombre:$reg[0]\nEdad:$reg[1]\nEmail:$reg[2]\n";
genera un resultado similar a:
Nombre:Daniel
Edad:22
Email:daniel@simba
Cuidado! no todas las funciones de Perl convierten de igual forma los
arreglos en escalares, por lo que debe provarse o investigar primero que
efectivamente en el contexto en que hagamos la conversión el resultado sea el
deseado.
Ejemplos:
Arreglo constante
@arreglo=(1,2,"hola",3,"adios");
inicializa el arreglo @arreglo con los elementos 1,2,"hola",3,"adios",
(todos escalares). la notación de los elementos entre paréntesis define un
arreglo constante, el equivalente a un numero o cadena constante cuyo valor
asignáramos a una variable pero en el contexto de los arreglos.
"Hash" constante
%hash=("llave1","dato1","llave2","dato2); #arreglo constante a hash
Inicializa el %hash con las llaves "llave1"
y "llave2" poniéndole como contenidos "dato1"
y "dato2" respectivamente. De hecho, no
especificamos un "hash constante" como en el caso del arreglo, sino
que especificamos un arreglo constante el cual pasa por una transformación de
clase para asignarse a un hash, de modo que la expresión es equivalente a:
@arreglo=("llave1","dato1","llave2","dato2); #arreglo constante a arreglo
%hash=@arreglo; #arreglo a hash
Cardinalidad de un arreglo
@arreglo=(1,2,3,4);
$elementos=@arreglo;
En este caso $elemento recibe el valor 4, pues son 4 los
elementos del arreglo, nótese que el índice máximo en el arreglo es de solo
tres, pues el primer elemento es el elemento 0 y Perl 4 regresa, con esta misma
expresión el índice máximo en lugar del número de elementos como Perl 5.
Asignación a arreglo constante
($a,$b,$c)=(1,2,3);
Esta expresión es equivalente a:
$a=1;
$b=2;
$c=3;
Pero debe tenerse cuidado, el siguiente código:
($a,$b,$c)=(1,2,3);
@a=($a,$b,$c);
$a=7;
$b=8;
$c=9;
Da al arreglo @a el valor del arreglo constante (1,2,3),
y no, como podría pensarse. (7,8,9). Cuando se genera un arreglo
constante se evalúan los elementos que lo forman como constantes escalares y se
le asigna después, para obtener resultados diferentes se deberá usar un
arreglo de referencias.
Arreglos con arreglos
@a=(1,2,3);
@b=(5,6,7);
@c=(@a,4,@b,8);
Estas expresiones generan tres arreglos, (1,2,3), (5,6,7) y
(1,2,3,4,5,6,7,8), y no, como podría pensarse un arreglo de arreglos,
cuando incluimos un arreglo dentro de otro, Perl "aplana" el arreglo
insertado como si insertara uno a uno todos sus elementos en la posición
indicada del arreglo que ha de contenerlos, para hacer arreglos de arreglos
deberemos usar referencias a estos.
2.2.3- Tipos Especiales de Datos
Llamo a estos "tipos especiales" porque nos permiten hacer cosas
inposibles con otros tipos, por ejemplo, el evitar ser "aplanado" en
los arreglos. Además, incluyo en esta sección a los Archivos, pues aunque su
sintaxis se parece a las de las variables, su funcionalidad es bien distinta.
Sin embargo, por tratarse de solo una introducción omito discutir sobre los
"type globs" que son la aproximación a las referencias que perl 4
implementa.
2.2.3.1- Referencias
Nota: Este tipo de datos es exclusivo de Perl 5, Perl 4 usaba los type globs
para realizar algunas de estas funciones, pero el proceso es mucho mas
complicado y obscuro.
Las referencias son el equivalente lógico de los apuntadores, son un tipo de
dato que no contiene información en si misma, sino que permite manejar
indirectamente los datos contenidos en otra entidad.
Las referencias, sin embargo, no forman una clase nueva de datos, sino que
son tratados como un tipo mas de escalar.
La definición de referencias se realiza por el operador referencia "\"
(backslash) , funciona de modo similar a "&" en C, y aunque a
diferencia de C no hay un operador de derreferencia la sintaxis para acceder al
dato original es muy sencilla.
A diferencia de C, los objetos que creemos mediante referencias no quedan
como cadáveres en memoria, Perl lleva un registro de las diversas referencias a
cada elemento en memoria y automáticamente lo destruye cuando descubre que
nadie hace ya referencia a él, de modo que pueden usarse las referencias sin
miedo, aunque con la precaución necesaria para no perder los datos que
almacenamos en ellas.
2.2.3.1.1- Creación
Podemos crear referencias de varias formas, las que considero mas sencillas y
útiles son:
Usando el operador de referenciación en una variable, o valor, en el caso de
una variable, es crear un medio alterno de acceder al valor de la variable.
$rescalar=\$escalar;
$rarreglo=\@arreglo;
$rhash=\%hash;
$rrutina=\&subrutina; #la programación de subrutinas la revisaremos mas adelante.
$globref=\*ARCHIVO; #solo revisaremos los type globs para usarlos como referencias a archivos.
Creando objetos "anónimos" que solo pueden ser accesados por medio
de la referencia.
Escalares
$rescalar=\"hola"; #referencia a la cadena anónima "hola"
Arreglos:
$rarreglo=[1,2,3]; # referencia al arreglo anónimo (1,2,3)
Hashes
$rhash={"llave1" => "dato1","llave2" => "dato2"};
Nótese que en esta representación usamos además el operador
"=>" que indica una pareja de llave y dato, las que se separan por
comas; esta notación también es valida para hashes convencionales, pero la
claridad que agrega es mejor utilizada al declarar hashes anónimos.
Como elementos de arreglos y/o hashes para formar estructuras de datos
complejas, al ser escalares, son elementos del arreglo sin mas complicación, de
modo que los arreglos a los que hacen referencia se mantienen intáctos.
Por ejemplo, para formar un hash que contenga arreglos de nombres de letras:
%rhletras={
"latinas" => ["a","b","c"],
"gregas" => ["alfa","beta","gamma"]};
Esta sentencia forma un hash donde las llaves son cadenas y los datos son
referencia a arreglos.
Pueden convinarse las referencias a arreglos y a hashes anónimos a voluntad
para formar una estructura de datos tan compleja como se desee.
2.2.3.1.2- Uso
De nada sirven las referencias si no podemos extraer y modificar los datos
contenidos en los elementos señalados por la referencia, en Perl la forma para
obtener el valor y para modificarlo es casi la misma, solo las abreviaturas de
las referencias para obtener el valor funcionaran de modo distinto cuando
tratemos de asignarles valor.
Aun cuando Perl tiene varias formas de "derreferenciar" la
información, solo discutiré dos por considerarlas las mas sencillas, sin
embargo, recomiendo una revisión al capitulo PERLREF de la referencia de Perl
5.X para una explicación mas detallada y a consciencia del manejo de las
referencias.
Uso "simbólico" de las referencias.
Por regla general, podemos usar las referencias como si se sustituyeran antes
de ejecutar el código (aunque en realidad, no sea así), de modo que el código:
$nombre="entero";
$entero=5;
$rentero=\$entero;
$$nombre=6;
$$rentero=7;
efectivamente cambia el valor de $entero de 5 a 6 y despues
de 6 a 7, del mismo modo podemos usar las referencias refiriéndonos a cualquier
otro tipo.
por ejemplo, para arreglos:
$rarreglo=[1,2,3,4] #crea arreglo anónimo (1,2,3,4)
$$rarreglo[2]="tres"; #modifica el arreglo anónimo a (1,2,"tres",4)
@$rarreglo=(); #limpia el arreglo anónimo
Uso con el operador de derreferencia.
Como una forma de abreviar el proceso para referencias a arreglos y hashes,
se añadió el operador -> que indica que el escalar es una referencia a hash
o arreglo y espera el índice después.
por ejemplo:
$rarreglo->[5]="algo";
coloca "algo" como el 6o elemento del arreglo al
que hace referencia $rarreglo.
$rhash->{"alfa"}="uno";
coloca la pareja ("alfa" => "uno") en
el hash al que hace referencia $rhash.
Uso abreviado del operador de derreferencia.
Se pueden obtener referencias a referencias, y se pueden hacer arreglos de
referencias, de modo que los arreglos multidimencionales se pueden elaborar con
la misma mecánica que en C.
$ra3d->[0]->[0]->[0]=1; #pone un "0" en la primera celda de un arreglo tridimencional
Nótese que al crear perl los arreglos de las dimensiones adecuadas automáticamente
no hay problemas de invasión de memoria no reservada (aunque un uso descuidado
puede ocupar cantidades demasiado grandes de memoria). Y esta aplicación se
considero lo bastante frecuente para implementar una abreviatura, de modo que la
expresión anterior es equivalente a:
$ra3d[0][0][0]=1;
Lo cual le da mas claridad de lectura al código (pero debe tenerse cuidado
de no confundirlo con un elemento de un arreglo, $ra3d es una referencia a un
arreglo de referencias y no un arreglo normal. Por claridad podríamos usar:
$ra3d->[0][0][0]=1;
Ejemplo: A continuación, coloco el listado de un programa que usa las
estructuras de datos que mencionamos anteriormente usando su potencia lo mas
posible sin que pierdan claridad, recomiendo que no solo se pruebe el programa,
sino que se elabore una versión en la que se exploren los puntos que
particularmente les resulten mas interesantes.
2.2.3.2- Archivos
Uno de los usos mas probados y comunes de Perl es el procesamiento de
archivos de texto para generar otros archivos (por lo regular de texto) que
implican cierto proceso de los datos leídos originalmente. Además, Perl no
conoce mas entrada que la proveniente de archivos (asociando la entrada, salida
y salida de errores a archivos "de ambiente") por lo que en cierta
medida ya hemos revisado el uso básico de archivos, solo que ahora le daremos
la explicación correspondiente a las entidades que vimos antes.
Como en cualquier otro lenguaje, el ciclo de uso normal de un archivo es:
Apertura Mediante "open" inicializa una variable de archivo
Uso Lectura secuencial por líneas
Cerrado Mediante close
Perl tiene implementaciónes para manejar archivos binarios y archivos
aleatorios, pero el uso de archivos de texto secuenciales es lo mas común en
UNIX, por lo que considero a este manejo como básico y le explico aquí.
Básicamente, se puede decir que hay tres tipos de archivos que se manejan de
modo muy similar:
-
Archivos comunes
-
Programas de los que capturamos la entrada o la salida
-
La entrada, salida o salida estándar de errores.
Estos tres tipos de archivos se manejan igual (con la limitación de que
algunos solo reciben lectura o escritura) y el momento en que se determina el
tipo verdadero del archivo, es en el momento de la apertura, por lo que ha
dedicado una sección exclusivamente a la apertura mientras que al uso y cierre
los he concentrado en la sección que siguiente a esta.
2.2.3.2.1- Apertura
La entrada, salida y salida de errores estándar, están abiertas por default,
pero en cualquier otro sentido se utilizan igual que cualquier otro archivo,
debe tenerse cuidado al planear los entubamientos de información para evitar
que nuestro programa espere por siempre una entrada que no le ha de llegar, sin
embargo no es necesario ser paranoico al respecto. Si tratamos de usarlas después
de cerrarlas el resultado de las lecturas será nulo y los intentos de salida no
tendrán efecto.
Los archivos convencionales se abren con la instrucción "open", la
cual es, por decir poco, muy interesante, en esta sección solo revisaremos su
uso básico.
open VARCHIVO,EXPRESION
VARCHIVO es la variable mediante la que usaremos el archivo,
no tiene indicador de clase y por convención la manejamos en mayúsculas.
EXPRESIÓN es, digamos, el nombre y modo en que habrá de
abrirse el archivo.
Los archivos, se darán con los nombres nativos del S.O., y los modos se
especifican con las siguientes cadenas al inicio del nombre (que imitan al shell).
<archivo abre "archivo" para lectura
>archivo abre "archivo" para escritura, borrándolo si existe.
>>archivo abre "archivo" para escritura, agregando la nueva
información al final
+<archivo abre "archivo" para lectura y escritura
+>archivo abre "archivo" para lectura y escritura borrando "archivo" al abrirlo.
|programa ejecuta "programa" y reasigna su entrada estándar a lo que
escribamos en el
programa| ejecuta "programa" y reasigna su salida estándar para
lectura de nuestro programa
Debe notarse que a diferencia de los archivos no se puede ejecutar programas
para lectura Y escritura.
Es importante notar que esta forma de ejecutar programas desde perl es muy
poderosa, pero no es la única, por lo regular se usa "system"
para ejecutar programas si la salida no nos importa o encerrando con las
comillas ` al comando si la salida es sencilla (un renglón) lo que evalúa
al comando como si fuese una expresión resultando en la salida del programa
(las comillas ' funcionan de distinta forma entre Perl 4 y 5 al ejecutar el
programa).
También se pueden abrir la salida y entrada estándar mediante open, pero
estas funciones así como la de cerrarlas puede tener consecuencias en la
ejecución del programa que son algo complicadas de explicar, por lo que
recomiendo que de desear manejar estas operaciones se use la referencia de Perl.
2.2.3.2.2- Uso y Cerrado
Básicamente, se puede usar un archivo de dos formas, por renglones,
(terminados por el carácter de vuelta de carro) o por carácter (byte).
Revisare solo la primera opción, porque el manejo por carácter tiene
implicaciones sobre la naturaleza del sistema de explicación muy larga y porque
son mas usuales los archivos de texto en las aplicaciones de Perl.
El operador que nos permite leer de un archivo es "<>"
teniendo la variable de archivo dentro de el, así por ejemplo:
$reng=<STDIN>;
Que, como ya hemos mencionado; lee un renglón de la entrada estándar y lo
coloca en la variable $reng, incluida la vuelta de carro que termine el
renglón.
Si el renglón termina con carácter de fin de archivo, este no se incluye en
el renglón, y lecturas sucesivas de STDIN esperaran
indefinidamente por una nueva entrada (así que debe tenerse cuidado al usar la
entrada estándar o confiar en el usuario).
STDIN es la variable de archivo que identifica la entrada estándar,
así como
STDOUT es la variable que identifica la salida estándar y
STDERR es la que identifica la salida estándar de errores.
@contenidos=<STDIN>;
Esta vez, estamos evaluando al operador <> en un contexto de
arreglo, y su comportamiento varia, como arreglo <> regresa un
arreglo de cadenas, donde cada cadena es un renglón del archivo, de modo que la
expresión anterior leerá todo lo que se introduzca en la entrada estándar y
lo coloca en el arreglo @contenido. Esta es una forma mas segura
de usar la entrada estándar, pues ahora cada elemento del arreglo equivale a
una lectura sucesiva de STDIN en contexto escalar.
Si sustituimos STDIN por cualquier otra variable de archivo (o valor
de variable de referencia a una variable de archivo) estaremos cargando el
archivo ya sea renglón por renglón o todo completo a variables de memoria.
Como caso especial, si evaluamos la expresión:
<ARCHIVO>;
cargaremos un renglón del archivo al que ARCHIVO hace
referencia y lo coloca en la variable $_ de la cual hablaremos mas
adelante.
A continuación, esta el listado de un programa sencillo realizado en perl
que demuestra la potencialidad de las estructuras de datos y lo básico del
manejo de archivos, este programa esta hecho para perl 4, pero no creo que tenga
problemas para correr bajo perl 5, sin embargo, debe recordarse que la ruta
hacia perl muy probablemente deberá cambiarse o deberá ejecutarse perl dándole
el programa como parámetro.
#!/usr/bin/perl
# Programa hecho en perl4 que crea un archivo de parejas de nombre y
# dirección IP tomando las direcciones de un archivo de la forma:
#
#131.178.80.32
#206.103.64.98
#ppp16-07.rns.tamu.edu
#130.178.80.20
#
# Etc, entiendase que los "#" no están en el archivo de entrada que
# tiene el nombre "salida" y que esta en el directorio activo.
open(DIR,"<salida"); # Abre el archivo de entrada
while($dir=<DIR>) # Lee todas sus líneas una a una
{
chop $dir; # les corta el fin de línea
open(NS,"nslookup $dir|"); # Invoca nslookup y recibe el resultado
undef(@inf,$nombre,$inf); # Destruye datos viejos
while($reng=<NS>) # revisa el resultado del nslookup
{
chop $reng; # quita fin de línea
$reng=~s/ //g; # quita espacios
@inf=split(":",$reng); # Hace un arreglo con los resultados
# que vienen como "Name: servidor.unam.mx"
# "Address: 132.248.100.100" por ejemplo.
if($inf[0] eq "Name") {$nombre=$inf[1]}; #Toma el nombre del servidor
if($inf[0] eq "Address") {$dir=$inf[1]}; #Toma su dirección IP
}
close NS; # Cierra los resultados y termina la ejecución de nslookup
if(defined($nombre)) # Si asigno valor a $nombre...
{
$hres{$dir}=$nombre; # Llena un Hash de la dirección y el nombre
print "%\n"; # Indica el éxito de la resolución
}
else
{
print "#\n"; #Indica el fracaso de la resolución
}
}
close(DIR); #Cierra su archivo de entrada
open(DIRS,">direcciones"); # Abre archivo de salida de resultados
print "\nimprimiendo resutlados...\n";
foreach $dir (keys %hres) # Para todas las llaves de %hres
{
print DIRS "$dir $hres{$dir}\n"; #imprime la pareja llave-valor
}
close(DIRS); # cierra su archivo de salida.
2.3- Operaciones Básicas
Como se habrá podido distinguir en los ejemplos vistos hasta ahora, Perl
cuenta con una amplia y muy interesante gama de operadores, en esta sección
daremos una rápida revisión a los mas importantes, confiando en que el uso de
la mayoría de los operadores será casi intuitivo.
Para obtener mayor información sobre los operadores, la sección PERLOP de
la referencia es la mejor fuente de información detallada.
Las variables, comillas, paréntesis y funciones con paréntesis tienen la
precedencia mas alta en perl, de hecho, Perl los interpreta mas como operadores
unarios que se comportan como funciones por la agrupación de los parámetros
sobre los que actúan. Después de estos, la precedencia de los operadores mas
usuales es:
Mas alta:
Posición Operador
no asocia ++ y --
derecha ! ~ \ y la versión unaria de + y -
izquierda =~ !~
izquierda * / % x
izquierda + - .
no asocia < > <= >= lt gt le ge
no asocia == != <=> eq ne cmp
izquierda &
izquierda | ^
izquierda &&
izquierda ||
no asocia ..
derecha ?:
derecha = += -= *= etc.
izquierda , =>
izquierda not
izquierda and
izquierda or xor
Mas baja.
2.3.1- Operaciones Aritméticas
Estas operaciones interpretaran al escalar como un numero (ya sea entero o
real) y operaran aritméticamente sobre el, toda cadena que no inicie con una
cifra será interpretada como un cero por estos operadores.
En orden de Precedencia tenemos:
++ y -- Son los operadores de auto incremento
y auto decremento tan conocidos para los programadores de C. Su uso se puede
resumir en la siguiente tabla:
Posición ++ --
Antepuesto Incrementa 1 antes de evaluar Decrementa 1 Antes de evaluar
Pospuesto Incrementa 1 después de Decrementa 1 después de
evaluar evaluar
Así pues de las expresiones:
$var=5;
print $var++;
print "\n$var\n";
obtenemos impresos los números:
5
6
Debido a que en el primer print, incrementamos $var, pero al evaluar antes
del incremento obtenemos su valor original, sin embargo, para evaluaciones
posteriores su valor ha sido incrementado.
** Es el operador de Exponenciacion, así 5**2 resulta en 25
(5 al cuadrado)
* Es el operador de Multiplicación
/ Es el operador de División
% Es el operador de Modulo
+ Es el operador de Suma
- Es el operador de Resta
A estos no les dedicare mayores explicaciones por ser típicos en todos los
lenguajes de programación y comunes a la matemática cotidiana.
Sin embargo, hay interesantes derivados de estos que vale la pena revisar.
Estos son los operadores de automodificación, estos se implementan al observar
que en muchos programas se requiere asignar a una variable el resultado de una
operación que involucra el antiguo valor de la misma variable. Estos operadores
proporcionan una forma resumida de operar una variable con otra y asignar a la
primera el resultado, por ejemplo:
Si queremos hacer un $total, que sea el $total
mas un $cargo particular podríamos escribir:
$total=$total+$cargo;
Y en Perl (al igual que en C) tenemos la opción de resumirlo como:
$total+=$cargo;
Nótese que el operador empleado fue "+=" que
indica que al valor actual de la variable, se le asignara su propio valor mas el
resultado de la evaluación de la expresión a al derecha del operador.
Así como se puede formar el operador de asignación con incremento, se
pueden utilizar la mayoría de los otros operadores susceptibles a actuar sobre
una variable, la única excepción no intuitiva es el de sustitución de
patrones en la variable, que usa una sintaxis distinta "=~"
que revisaremos al final de esta sección.
2.3.2- Operaciones Lógicas
Indispensables para las condiciones de todo ciclo de control son las
operaciones lógicas en los lenguajes de programación, y en este contexto me
permitiré incluir las funciones de comparación.
Las operaciones de comparación entre valores se dividen en aritméticas y de
cadenas, y hay que tener especial cuidado en utilizar la familia correcta so
pena de obtener resultados no deseados.
Operación Aritmética Cadena
Mayor que > gt
Menor que < lt
Mayor o igual >= ge
Menor o igual <= le
Igual == eq
Diferente != ne
Compara <=> cmp
La operación "Compara" regresa un valor de -1,0 o 1 dependiendo si
el valor a la izquierda es menor, igual o mayor (respectivamente) que el de la
derecha del operador, nótese que si deseáramos evaluar esto de forma lógica
seria equivalente al operador "Diferente".
Debemos recordar que Perl considera como verdaderos los valores no nulos
(cadenas) y diferentes de cero.
Además, tenemos los operadores lógicos que permiten cambiar el valor de
verdad de una expresión:
Operador Función
! Negación Lógica
- Inverso aditivo (cambia el signo de un numero)
~ Negación Bit a bit de un valor
&& Conjunción lógica de los operandos
|| Disyunción lógica de los operandos
& Conjunción bit a bit de los operandos
| Disyunción bit a bit de los operandos
^ Or exclusiva bit a bit
.. Si evaluado como escalar regresa alternativamente verdadero y
falso. Como arreglo genera un arreglo a partir de los limites
de un rango.
not Negación lógica de la expresión a su izquierda (muy baja
precedencia)
and Conjunción lógica de baja precedencia
or Disyunción lógica de baja precedencia
xor Disyunción exclusiva lógica (precedencia mínima)
2.3.3- Operaciones con Cadenas
Así como los operadores aritméticos forzan a la variable a comportarse como
de tipo entero o real, así los operadores de cadena forzan a los escalares a
comportarse como cadenas, de modo que los números son automáticamente
convertido en cadenas (son su precisión completa) al aplicarles estos
operadores.
Las operaciones a realizar con cadenas, por lo regular se manejan como
funciones en vez de como operadores, con la excepción de las siguientes:
Operador Función
=~ Especifica la expresión a afectar por búsqueda de patrones
regulares (ver sección siguiente).
!~ Especifica la expresión a afectar por búsqueda de patrones
regulares, pero evalúa negando el resultado lógico de la
operación.
. Concatenación, todas las cadenas operadas por "." forman una
sola cadena.
x Es el operador de repetición, en contexto escalar toma la
cadena de la izquierda y la repite el numero de veces que el
numero de la derecha indica. En contexto de lista, toma la
lista a su izquierda y la repite las veces que indica el
operador a su derecha en una nueva lista.
++ Autoincremento, modifica el valor de los elementos de una
cadena de modo que se incrementen sin cambiar su naturaleza,
por ejemplo ++"zzz" -> "aaaa"; ++"99" -> "100" , ++"a9" ->
"b0", etc.
2.3.4- Operaciones Regulares
Este sección se dedica a dar una rápida revisión a las operaciones de
sustitución y reconocimiento de patrones (definidos por lenguajes regulares)
que son de las características mas utilizadas y útiles de Perl, aun cuando no
sean tan sencillos de comprender en un primer acercamiento, recomiendo al lector
que dedique algún tiempo extra al estudio de la construcción de las
expresiones para describir los patrones a buscar por su cuenta, pues puede
obtener grandes beneficios de este esfuerzo, la referencia es hasta donde se, la
mejor fuente de información a este respecto, en las secciones de operadores
PERLOP y de expresiones regulares PERLRE.
Los operandos para utilizar las expresiones regulares son:
Operado Función
r
=~ Especifica que la cadena a la izquierda del operando es la que
pasara por el proceso ya sea de sustitución, o de búsqueda.
!~ Especifica que la cadena a la izquierda del operando será la
afectada, y hace que la expresión en su conjunto regrese el
negado del valor lógico resultante.
// Delimita un patrón regular.
Por defecto, las operaciones de búsqueda de patrones y sustitución actúan
sobre el parámetro default $_ , y al evaluarlas modifican su valor y regresan
el numero de sustituciones u ocurrencias encontradas.
La formación y manejo de las expresiones regulares escapa a los alcances de
este documento, así que solo mencionare como utilizar estos operadores para
realizar substituciones y su derivación para buscar algún patrón.
Para realizar búsquedas, la sintaxis básica es:
s/PATRÓN/CADENA/g
Que hace la substitución de todas las ocurrencias de PATRÓN
en CADENA. En esta forma, actuara sobre el parámetro default $_
y regresara el numero de substituciones que haga o cero si no encuentra el patrón.
Así, para cambiar todas la ocurrencias de "Hola"
por "Saludos" en una cadena llamada $cadena
(en vez de en $_) deberemos escribir:
$cadena=~s/Hola/Saludos/g;
Por el contrario, si deseamos saber cuantas veces aparece "Hola"
en cadena y colocar este valor en $cuenta deberemos escribir:
$cuenta=( $cadena=~s/Hola/Hola/g );
Nótese que en realidad no realizo la operación de búsqueda (que si existe)
sino la substitución por la misma cadena, para evitar introducir nuevas
operaciones.
Por ultimo, si deseamos determinar si no se encuentra la cadena, (pues para
determinar si se encuentra bastaría usar el valor de veces que se encontró la
cadena como condición lógica, pues evaluara como verdadero si es distinto de
cero y falso si es cero) usaremos el operador !~
if($cadena!~s/Hola/Hola/g)
{
print "No se encontraron \"Hola\"s\n";
}
Como ya se menciono, para un tratamiento serio de las cadenas regulares y los
operadores para utilizarlas hay que consultar la referencia de perl en las
secciones de PERLOP y PERLRE.
2.3.5- Operaciones Misceláneas
A continuación describo brevemente la función de otros operadores que
resultan útiles en condiciones particulares.
Operador Función
?: El operador condicional, es una forma de if resumida en una
sola expresión, tiene tres operandos, op1?op2:op3 donde op1 es
una expresión que evalúa falsa o verdadera, Si evalúa
verdadera, se evaluara y la expresión regresara el valor de
op2, si por el contrario, op1 evalúa falso, es op3 la que
determina el valor final de toda la expresión.
, Como el operador , de C, si se usa entre dos expresiones,
evalúa la de la izquierda, desecha el valor obtenido y evalúa
la expresión de la derecha regresando el valor así obtenido.
=> Este operador funciona primordialmente igual que la coma, pero
es exclusivo de Perl 5, forzando en algunas versiones que el
operando de la izquierda sea una cadena (recuérdese su uso para
declarar arreglos asociativos).
Además, tenemos los signos de puntuación, que a diferencia de otros
lenguajes son considerados operadores de Perl.
Básicamente, tenemos 4 tipos de símbolos que nos sirven para agrupar otros
símbolos para darles una interpretación especial, son:
Símbolo Función
'' Especifica valores literales (cadenas), no hace substituciones.
"" Especifica valores literales, haciendo substituciones.
`` Ejecución, ejecuta el comando contenido entre ` después de hacer
las substituciones indicadas.
// Suelen usarse para delimitar expresiones regulares, hace
substituciones con la operación s//.
Entiendase por substitución el reemplazar el nombre de variables por los
valores de estas, así como los caracteres de escape; por ejemplo:
$saludo="echo hola";
print '$saludo'."\n";
print "$saludo"."\n";
print `$saludo`."\n";
print "adios\n";
Genera como resultados algo similar a:
$saludo
echo hola
hola
adiós
Como se puede ver , en todos los casos imprime un fin de línea "\n"
después de cada cadena (o expresión), la primera, es, literalmente la cadena
dada; la segunda, es la cadena donde se ha substituido el nombre de la variable $saludo
por su valor; la tercera, es el resultado de la ejecución del comando
especificado en la cadena, donde se substituyó el nombre de una variable por su
valor.
Perl 4 no regresa valor cuando utilizamos las comillas para ejecutar un
programa o comando, es decir, lo ejecuta sin capturar su salida, de modo que
esta irá a la misma salida estandar del script que lo invocó.
Alguna experimentación con el uso de estos operadores es altamente
recomendable, pues las convenciones de cada programa para entregar sus
resultados a algún dispositivo o a alguna salida distinta de la standard pueden
ocasionar resultados poco previsibles.
2.4- Estructuras de Control
Las estructuras de control básicas de Perl y su sintaxis son:
IF:
if (EXPRESIÓN) BLOQUE
if (EXPRESIÓN) BLOQUE else BLOQUE
if (EXPRESIÓN) BLOQUE elseif (EXPRESIÓN) BLOQUE ... else BLOQUE
WHILE:
WHILE (EXPRESIÓN) BLOQUE
ETIQUETA WHILE (EXPRESIÓN) BLOQUE
FOR
for (EXPRESION;EXPRESION;EXPRESION) BLOQUE
ETIQUETA for (EXPRESION;EXPRESION;EXPRESION) BLOQUE
FOREACH
foreach VARIABLE (LISTA) BLOQUE
EXPRESIÓN es toda instrucción de PERL que evalúa en un
solo valor, básicamente se emplean como inicializaciones y condiciones de las
estructuras.
BLOQUE es un conjunto de expresiones encerradas en {},
nótese que todos los bloques, sin excepción inician con { y
terminan con }.
ETIQUETA se refiere a etiquetas en el código, estas son
nombres seguidos de ":", que permiten identificar a
algún bloque en particular.
Para modificar la ejecución de los ciclos tenemos las siguientes
instrucciones:
next Esta interrumpe el flujo del ciclo iniciando de inmediato
ETIQUETA la siguiente iteración. Si no se especifica la ETIQUETA
afecta al ciclo mas interno.
last Esta interrumpe el flujo del ciclo y pasa el control a la
ETIQUETA primera instrucción después del bloque del ciclo.
redo Esta reinicia la ejecución del ciclo, sin reevaluar la
condición.
Los programas a lo largo de este documento son ejemplos del uso de estas
estructuras de control, por lo que no pongo ningún ejemplo especifico en esta
sección.
2.4.1- Manejo de subrutinas
Como en cualquier lenguaje estructurado, Perl tiene la capacidad de generar
nuevas funciones o procedimientos, pero perl tiene varias peculiaridades en la
forma de definir sus funciones,Ademas de ser estas las que sufrieron mas cambios
entre la versión 4 y 5.
Características de las funciones en la versión 4 de Perl
Para su ejecución, se les tiene que señalar explícitamente, anteponiendo
el símbolo de & al nombre de la función (algo similar a la
clase de una variable), las referencias a una función eran siempre a través de
typeglobs por lo que es incomodo e inusual usarlas. Reciben siempre un único
parámetro, el arreglo default @_ en el cual debían ir todos
los parámetros que de desearan, sin posibilidad forzar el paso de ciertos parámetros
a la función, para obtener los valores se suele hacer un $parametro=shift
@_; por parámetro.
Características de las funciones en la versión 5.002 y superiores
Debe notarse que en versiones anteriores de perl no todas están
implementadas, en particular, los prototipos de función no están implementadas
sino hasta la versión 5.002.
Si un procedimiento o función ya ha sido declarado y se ejecuta como
instrucción en una línea puede omitirse el símbolo & al
inicio de su nombre. Es mas, siendo que perl no requiere el uso de paréntesis
al inicio y final de una función, los parámetros pueden seguir al nombre de la
función como para cualquier otra función nativa de Perl.
La verdadera utilidad de declarar nuestras funciones es el reducir la
cantidad y complejidad de la revisión debe hacer de los valores que recibe como
parámetros (dejando a Perl una mayor parte del trabajo), por ejemplo, si
construimos una función que forzosamente requiera una clase de valor como parámetro
(por ejemplo, una referencia) podemos definirla de modo que solo acepte parámetros
que cumplan con esta característica, de modo que perl genere un error en caso
de que traten de pasarle un dato de tipo distinto, en vez de que nosotros
tengamos que verificar si el escalar recibido corresponde o no a una referencia.
A continuación revisaremos la definición de las subrutinas y funciones,
Perl no hace mayor diferencia entre estas, de hecho se manejan igual y usaremos
los términos indistintamente a lo lago de esta sección.
Sintaxis básica:
Declaraciones; especifican que habrá una función de este nombre pero no
especifican su código, requieren que posteriormente se haga la definición.
sub NOMBRE; Con parámetros indeterminados (estilo Perl4)
sub NOMBRE(PROTOTIPO); Especificando clase de parámetros, Perl5.002 o
Definiciones Se especifica el código de una función, y si se desea, también
su nombre y prototipo (tipo y numero de parámetros).
sub NOMBRE BLOQUE; Definición de la función, parámetros
indeterminados
sub NOMBRE (PROTOTIPO) Definición con parámetros
BLOQUE
$subref=sub BLOQUE Especifica código y regresa una referencia
a el, en este caso $subref es una
referencia a una función de parámetros
indeterminados cuyo código esta contenido
en BLOQUE. No lo discutiremos a detalle por
considerarlo una aplicación especial de
referencias y subrutinas.
Los elementos que empleo en esta definición son:
NOMBRE Es un identificador valido para perl (palabra formada de
números, letras y bajo-guion con las mismas reglas que C,
Pascal y demás. Es el nombre con el que se podrá invocar la
función, si se omite, en vez de definir o declarar la
función se generara una referencia a una función anónima.
BLOQUE Es un bloque de código, al igual que en los ciclos de
control, siempre inician con "{" y terminan con "}", dentro,
puede haber cualquier serie de sentencias de perl, y
determina un espectro de validez para las variables que se
declaren locales con my.
PROTOTIPO Es una cadena que indica la clase y una característica de
cada parámetro que la función recibirá.
Comencemos revisando como crear un procedimiento que no tome parámetros.
sub sencillito
{
print "Yo soy un procedimiento sencillito\n";
}
Este procedimiento se invocara con el identificador "sencillito"
o "&sencillito" , ejecutara su código y terminara
sin regresar un valor. Una versión de este que ahora es función (pues regresa
un escalar) será:
sub sencillo
{
print "Yo soy una función sencilla\n";
return "sencilla";
die("no me muero\n");
}
Este procedimiento, regresa la cadena "sencillo"
después de imprimir el mensaje especificado en el print, nótese que despues
del return no continua su ejecución y que al igual que "sencillito"
no pone atención a los parámetros, que podrían de hecho, pasársele o no.
return es la palabra reservada que hace que un procedimiento o función
termine y regrese un valor, si se usa el return sin especificar valor o si se
llega al final del código sin encontrar return regresa un nulo.
Si deseamos tomar parámetros, pero no aun hacer uso de los prototipos,
debemos tomar en cuenta que todos los parámetros vienen empacados en @_
el arreglo default, y que para obtenerlos podemos usar las siguientes
expresiones:
($primero,$segundo,$tercero)=@_; #Obtiene tres escalares del arreglo (nótese que si tercero fuese arreglo, absorbería el resto de los escalares en @_)
$primero=shift @_;
$segundo=shift @_;
$tercero=shift @_; #También obtiene tres parámetros de @_, pero este también los retira de @_, pues el operador shift toma el primer elemento de una lista y lo elimina, regresándolo como resultado de la expresión.
La ventaja del segundo método es que se retiran de @_ los
elementos que eran parámetros para la función que hacemos, de modo que @_
puede contener mas información útil para algún otro propósito.
Las referencias son de gran utilidad para manejar todo tipo de datos como parámetros
a una función, de hecho, la única posibilidad de pasar un arreglo a un
procedimiento sin que el arreglo pierda su identidad requiere el uso de
referencias, aunque como veremos, este requisito puede esconderse al invocar la
función (no así al implementarla).
De cualquier modo, si recibimos referencias como parámetros solo deberemos
darles el manejo de escalares para extraerlas de @_ y usarlas
como siempre.
La manera de "ocultar" la necesidad de referencias en el momento de
invocar la función implica el uso de prototipos.
Los prototipos pueden ser de gran utilidad para evitar errores de programación
al invocar funciones con los parámetros equivocados, por lo que a pesar de no
ser un tema básico lo incluyo en este documento.
Las cadenas del prototipo se componen de los siguientes símbolos:
$ Escalar
@ Arreglo (toma todos los parámetros siguientes en el arreglo)
% Arreglo Asociativo (también absorbe todos los parámetros)
\ El identificador del siguiente parámetro deberá iniciar cón ($,@ o %).
; Los siguientes parámetros son opcionales
Así por ejemplo, las siguientes definiciones especifican:
sub biescalar ($$); #recive dos escalares
sub escarreglo ($@); # toma el primer parámetro individual y todos los demás en un arreglo.
sub tomahash (%); #los parámetros recibidos forman un hash
sub dosotresesc ($$;$); #dos escalares obligatorios y un tercero opcional.
sub arreglos (\@$\@); #un arreglo, un escalar y después otro arreglo, sin que se mezclen.
Debe notarse que si se utilizan los parámetros de clase arreglo y hash sin
"\" estos serán el ultimo parámetro a recibir, pues tomaran el resto
de los parámetros dentro de si, de modo que si se desea tomar mas de un arreglo
o hash como parámetro se deberá especificar la clase con "\". Nótese
que el uso de "\" tiene como efecto colateral que se recibe una
referencia al arreglo, hash o escalar, la que esta garantizada a ser una
referencia al tipo especificado; nótese que no por eso el parámetro que acepta
la función es una referencia, por el contrario, debe ser una variable de la
clase especificada.
Este mecanismo, nos permite controlar la clase de la variable que nos dá el
velor que recibimos como parámetro.
A continuación, veremos un ejemplo sencillo del uso de prototipos y
funciones en un programa que especifica una función que debe recibir dos
escalares e imprime un saludo con ellos.
#!/usr/bin/perl
print "hola\!\n";
sub rutina ($$)
{ $nombre=shift @_;
$direccion=shift @_;
print "Hola $nombre\nVivies en $direccion\n";
return 345;
}
print "Dame tu nombre\n";
$res[0]=<STDIN>;chop $res[0];
print "Dame tu direccion\n";
$res[1]=<STDIN>;
chop $res[1];
print rutina $res[0],$res[1] ."\n";
Revísese con cuidado este programa y experiméntese con el, de modo que el
siguiente programa (una modificación del anterior que explota las características
de prototipos y de referencias para recibir dos arreglos y un escalar como parámetro)
pueda comprenderse con facilidad.
#!/usr/bin/perl
# Definición de la subrutina "rutina"
sub rutina(\@\$\@)
{
($runo,$rsaluda,$rdos)=@_;
#(@uno+0>@dos+0)?print "uno mayor que dos\n":print "dos mayor o igual a uno\n";
print "$$runo[0],\n";
print "$$rsaluda\n";
print "$$rdos[0],$$rdos[1],\n";
return 345;
}
#Programa principal
#Primer nombre
@res=("Daniel","Sol","Llaven");
#Segundo nombre
@re=("David","Jonathan","Sol","Llaven");
$algo="hola";
print rutina(@res,$algo,@re) ."***\n";
Este segundo programa define una función con prototipos que utilizan
extensivamente "\", de modo que no solo requiere que
sus parámetros sean un arreglo, un escalar y un segundo arreglo, sino que estos
deben ser variables de las clases @, $ y @
respectivamente (por ejemplo, no aceptara una cadena constante en vez de $algo
en la ultima línea).
Observando el código, vemos que los parámetros se reciben como referencias
y se usan como tales, pero que en el programa principal se pasan los parámetros
como variables de los tipos especificados en el prototipo (de hecho, "rutina"
no aceptará ninguna otra cosa).
Espero que este ejemplo ayude a entender los conceptos de prototipos y de
subrutinas, en realidad, no suele hacerse hincapié en estos temas por no ser
necesarios, dada la libertad que perl nos da, pero en el contexto de desarrollos
medianos a grandes pueden ser muy valiosos para evitar errores de difícil
detección.
En este capitulo, como en todos los demás, es indispensable que el lector
haga programas de ejercicio de su propia inspiración con el tiempo y la
libertad de probar y experimentar los temas que revisamos aquí.
2.5- Operaciones con archivos
Al menos en la UNAM, Perl se utiliza principalmente con dos propósitos, el
primordial es para implementar interfaces para servicios de red, y el segundo,
es el proceso sistemático de grandes archivos de texto (bitácoras de operación,
control de usuarios, etc.) debido a su facilidad para el manejo de archivos y si
flexibilidad en el manejo de datos capturados de textos (formato muy común de
la información en UNIX, que tiene la única desventaja de hacer crecer un poco
mas los archivos, con las ventajas de ser interpretables con herramientas mas
variadas, e incluso a simple vista, con la posibilidad de comprimirse con gran
eficiencia).
Debo aclarar desde ahora, que el manejo de archivos binarios, no es uno de
los puntos fuertes de perl, y que aunque puede hacerlo, no solo no es común,
sino que resulta mejor utilizar programas hechos en C para proveer la manipulación
de este tipo de archivos, es por esto, que este tema solo lo tocaremos
brevemente, y nos enfocaremos al uso de archivos de texto.
Las operaciones básicas para trabajar con archivos son:
open(VARCH,ARCHIVO);
<VARCH>;
close(VARCH);
Que respectivamente abren, leen, y cierran el archivo. Donde:
VARCH Es la variable que identificara al archivo, no se le pone
identificador de clase y por convención, se utiliza con
mayúsculas.
ARCHIVO Es el nombre del archivo a utilizar, precedido de símbolos
que indican el modo de uso del archivo.
2.5.1- Apertura y cerrado
Los símbolos con los que especificamos el modo de operación de un archivo
son:
Símbolo Significado
> El archivo se abrirá para escritura (borrándolo si existe)
< El archivo se abre para lectura
>> El archivo se abrirá para escritura agregando al final
+< El archivo se abrirá de lectura escritura
+> El archivo se abre para lectura y escritura vaciandolo primero
El uso de archivos de lectura y escritura lo revisaremos mas adelante, pues
involucra algunos procesos adicionales al manejo convencional.
Los "archivos" que no requieren ni abrirse (ni cerrarse) son, como
es de esperarse, la entrada estándar, la salida estándar y la salida de
errores, sin embargo, si pueden ser cerrados y abiertos con open y close, para
esto se utilizan como nombre de los archivos (sin mas símbolos):
>- Abre la salida
estándar
- Abre la entrada
estándar.
Si no se utiliza ningún símbolo sino solo el nombre del archivo a utilizar,
este archivo se abre para lectura.
La función open regresa al evaluarse el valor de la variable de archivo, o
nulo si no se puede abrir el archivo en el modo deseado, de modo que si se
utiliza como condición lógica resultara verdadera en caso de lograr abrir el
archivo.
Algunos ejemplos de apertura de archivos son:
open(ETCP,"</etc/passwd");
Apertura de lectura únicamente del /etc/passwd asociado a la variable de
archivo ETCP
open(SAL,">salida.txt");
Creación del archivo "salida.txt" en el directorio desde donde se
invoque el programa, solo de escritura y asociado a la variable SAL.
El cerrado de los archivos en una operación recomendable, pero no necesaria,
en el caso de los archivos, garantiza que todos los buffers han sido vaciados (o
regresa falso en caso de que haya algún error).
2.5.2- Lectura de datos
La lectura de la información contenida en los archivos, como ya habíamos
mencionado antes, se realiza con la operación <>, en esta
ocasión la veremos mas a detalle.
El operador <> realiza la lectura de texto de el
archivo, cuya variable se coloca entre < y >.
Ahí es evaluado en un contexto de escalar, regresa un renglón, incluyendo el
carácter de fin de línea. Si se evalúa en contexto de lista, regresa un
arreglo de todos los renglones del archivo incluyendo sus terminadores de línea.
A manera de ejemplos:
$reng=<ENTRADA>;
Carga una línea del archivo asociado a ENTRADA (que debe ser de lectura).
@contenido=<ENTRADA>;
Toma todas las líneas del archivo y las coloca como elementos consecutivos
del arreglo @contenido.
Debe mencionarse, que cuando se evalúa el operador <>
para un archivo, el puntero de posición en el archivo va avanzando, de modo que
en aplicaciones sucesivas (de contexto escalar) recibiremos renglones sucesivos.
Y si se aplica en contexto de arreglo, una evaluación sucesiva no regresara mas
renglones.
Además, el operador <> tiene la propiedad de tomar
valor por defecto cuando no se especifica que archivo debe usar, esta característica
tiene dos modalidades:
Usar el ultimo archivo abierto:
El operador <> continua trabajando con el ultimo
archivo al que reavisamos, por ejemplo:
open(ENT,"<entrada.txt"); #trabaja con el archivo entrada.txt
while(<>) #para toda línea del ultimo archivo abierto
{
print; # se imprime.
}
En este programa utilizamos varios parámetros default de las funciones:
en el while(<>) vemos que el operador <>
no tiene ningún identificador de archivo, y sin embargo funciona como si se
hubiese puesto <ENT> en vez de <>,
esto es debido a que <> toma por default al ultimo archivo
abierto como archivo default.
En segundo lugar, vemos que ni asignamos el valor que regrese <>
a ninguna variable ni especificamos que variable debemos imprimir en el print.
En ambos casos se esta utilizando el parámetro default $_,
<> por defecto asigna el valor que resulte a $_ y print
por defecto imprime a la salida estándar el valor de $_.
El otro modo de obtener los valores de archivos por defecto a trabajar con <>;
supongamos un programa de nombre imprime.pl con el siguiente código:
while(<>) #para toda línea del archivo default
{
print; #imprimirla
}
Aparentemente este programa carece de función pues no especifica ni abre
ningún archivo para leer, el modo de especificar que archivo debe usarse es por
medio de parámetros, de modo que el programa debe ejecutarse como:
imprime.pl entrada.txt
Para que imprima todas las líneas del archivo entrada.txt.
En resumen, si no se especifica cual archivo deberá de usarse para leer
información con la operación <> Perl tratara de
utilizar:
-
El ultimo archivo abierto por open
-
El siguiente parámetro como nombre de archivo a abrir y procesar como de
lectura.
Nótese que si al segundo programa, imprime.pl, se le da mas de un parámetro,
recorrerá todos los parámetros como nombres de archivos a abrir para usar con <>.
Esto provee un método muy breve para hacer programas que procesen los archivos
cuyos nombres se pasen como parámetros.
Deba tenerse cuidado al emplear las variables de default en un programa, y no
recomiendo que se usen en programas grandes o de lógica compleja, pues estos
valores son fácilmente afectables por otras operaciones y se requiere un
conocimiento muy profundo de perl para conocer todas las peculiaridades del
lenguaje en cuanto a estas variables especiales. Así pues, recomiendo usarlas
solo para hacer mas simples los programas que ya sean sencillos por si mismos.
2.5.3- Escritura
Una vez que hemos abierto un archivo para escritura, la forma mas común de
escribir en el, es mediante la función print, de hecho, print esta diseñada
para escribir a archivos, pero el archivo que utiliza por defecto para escribir
es el de la salida estándar STDOUT, la sintaxis para especificar que archivo
debe utilizarse es:
print ARCHIVO LISTA
donde:
ARCHIVO es la variable de tipo archivo donde se dirigirá la
salida
LISTA es una lista con los valores a imprimir, (si solo es un
escalar se interpreta como una lista de un solo elemento, pero evita que al
imprimir listas se obtengan los números de su cardinalidad en Perl 5).
Si se desea emplear las variables de archivo guardadas en arreglos, hashes o
como resultado de expresiones se les deberá poner en un bloque de código
(entre {}) para mas información consultar la función print en
la referencia de Perl (sección PERLFUNC).
Así pues, para crear y llenar un archivo con los primeros 100 números
enteros:
open(AS,">enteros.100");
for($c1=0;$c1<100;$c1++) # de 0 a 99 (perdón si 0 no es entero)
{
print AS "$c1\n"; #escribe al archivo de AS el numero.
#contenido en $c1 y fin de línea.
}
Nótese que si se usan paréntesis alrededor de los parámetros del print muy
probablemente Perl pensara que toda la expresión entre paréntesis especifica
el valor a imprimir resultando en un error, por lo que recomiendo se evite el
uso de paréntesis cuando se usa print para escribir a archivos.
2.5.4- Saltos y proceso binario
Antes que nada debo indicar que las funciones que revisaremos aquí las
incluyo, no por considerarlas básicas para la programación, sino por su gran
utilidad, y para que se conozca el modo en que perl puede lidiar con archivos
binarios.
Sin embargo, si se pretende desarrollar algo usando estas herramientas
recomiendo ampliamente una saludable dosis de experimentación y prototipaje
para evitar demoras posteriores, además, será indispensable tener la
referencia a su lado.
Así que siéntase el lector con la libertad de saltar esta sección y
continuar con la 2.6, que le resultará mas útil al corto y mediano plazo.
Comencemos por la forma en que perl 5 (todo lo que tratare en esta sección
no funciona o funciona de modo muy distinto en perl 4) puede leer, escribir y
manejar información binaria, que llamaremos empaquetada:
Perl maneja la información en tipos abstractos, que nos ahorran gran trabajo
de programación al momento de usarlos, desafortunadamente, esto no ayuda en el
momento de querer leer tipos de datos que vienen como una serie de bits dentro
de bytes. La solución que perl da a este problema es mas o menos sencilla, pero
requiere algún entendimiento de ,que es una cadena.
Una cadena es una serie de caracteres, como el lector debe ya saberlo, pero
cada carácter es en sí mismo representado por un byte; así que si no somos
muy exigentes con los caracteres que pueden o no ser incluidos en una cadena,
podríamos decir que cualquier serie de valores representados en bytes pueden
interpretarse como una cadena. Pues bien, este es el concepto que Perl explota.
Un empacado de información lo manejaremos como un escalar que para fines prácticos
es de tipo cadena. Pero que en vez de generar con el operador "."
y contener texto común y corriente, contiene una serie de caracteres (que no
necesariamente producirán resutlados coherentes si tratamos de imprimirlos) que
en si mismos contienen información que nos interesa recuperar.
El proceso de desempacado de la información consiste en indicar a perl que a
partir de una cierta cadena empacada debemos extraer un arreglo de valores a
partir de un formato que deberemos especificar. La contraparte de este proceso
es el generar nuestra cadena empacada a partir de un arreglo de valores y la
misma información de formato que pretendemos leer después.
Bajo este enfoque, Perl puede manejar perfectamente bien un archivo basado en
registros, pero tendría serias dificultades para manejar un archivo que no
codifique toda su información en un patrón fijo, para esto será muy
importante la capacidad de brincar en la posición del archivo.
La sintaxis de las funciones que empaquetan y desempaquetan la información
son las siguientes:
pack($FORMATO,@VALORES);
Esta función regresa la cadena empacada con el formato descrito por la
cadena $FORMATO que contiene los valores presentes en el arreglo
@VALORES.
unpack($FORMATO,EXPRESION);
Esta es la contraparte de pack, usando el formato especificado por $FORMATO
extrae los valores de la cadena que resulte de EXPRESIÓN
(probablemente la variable que contiene la variable) y regresa al evaluarse un
arreglo de valores de los tipos especificados.
Para especificar el formato en el que se empaquetara o con el cual
desempaquetar la información se usa la siguiente sintaxis:
Simbolo Significado
A Cadena ASCII rellenada con espacios a la derecha.
a Cadena ASCII rellenada de nulos a la derecha.
b Cadena de bits (Orden ascendente de bits)
B Cadena de bits (Orden descendente de bits)
h Cadena de nibbles (en hexadecimal con el nibble bajo primero)
H Cadena de nibbles (en hexadecimal con el nibble alto primero)
c Un valor de carácter con signo
C Un valor de carácter sin signo
s Un entero corto con signo
S Un entero corto sin signo
i Un entero con signo
I Un entero sin signo
l Un entero largo con signo
L Un entero largo sin signo
f Un punto flotante en formato nativo de Perl
d Un doble en formato nativo de Perl
x Un byte nulo
X Regresar un byte
@ Llenar de nulos hasta la posición absoluta
Después de cada uno de estos se puede colocar un numero que indicara cuantos
elementos de este tipo esperamos, a excepción de los del primer bloque
(cadenas) donde el numero indicara la longitud de la cadena a interpretar como
un solo elemento.
Todos estos especificadores funcionan tanto a la entrada como a la salida (e
idealmente usamos la misma cadena de formato en el empaquetado y en el
desempaquetado.
Así por ejemplo, para leer de una cadena empaquetada los bits, suponiendo
que estos estar ordenados del menos significativo (al principio) al mas
significativo (al final), y que buscamos leer 32 bits será:
($cadbit)=unpack("b32",$paquete);
Nótese que al escribir ($cadbit) estamos asignando al
escalar $cadbit el primer elemento del arreglo generado por unpack,
es decir, el primer elemento generado.
Por otro lado, para empaquetar y desempaquetar en una cadena 4 enteros
podemos escribir:
$paquete=pack("i4",(34,-43,127,512));
@enteros=unpack("i4",$paquete);
Debe recordarse que con las cadenas el numero que se pone a continuación del
carácter (aAbBhH) indica la longitud de las cadenas, pero con los demás tipos
indica cuantos elementos de ese tipo esperamos.
Ahora, para leer byte por byte un archivo (los cuales podemos concatenar con
los el acostumbrado operador ".") utilizamos la función
getc
getc ARCHIVO;
Esta función regresa el siguiente carácter del archivo al que ARCHIVO
hace referencia, o una cadena vacía (falso lógico) si llegamos al fin de
archivo.
Por ejemplo, hagamos un diminuto programa que nos muestre los valores
hexadecimales y binarios de todos los bytes de un archivo:
#!/usr/bin/perl
#programa de prueba de las capacidades de desempaquetado
#y proceso binario de archivos en Perl5
open(AE,"<$ARGV[0]");
$contador=0;
print "offset\thexa\tbinario\t\tcaracter\n";
while($ch=getc(AE))
{
$binario=unpack("B8",$ch); #deseamos el bit mas significativo a la izquierda
$hexa=unpack("H2",$ch); #nibble mas significativo primero
print "$contador\t$hexa\t$binario\t\"$ch\"\n"; #$ch es el carácter original
$contador++;
}
close(AE);
Este programa también intenta imprimir el carácter leído con fines didácticos,
si se emplea con archivos auténticamente binarios recomiendo que se elimine $ch
de las variables a imprimir.
La característica comúnmente utilizada del proceso de archivos binarios que
resta por revisar es la lectura aleatoria de archivos; esto se logra cambiando
arbitrariamente la posición del apuntador de archivo mediante la función search.
Esta función trabaja por bytes, de modo que es de muy poca utilidad cuando
trabajamos con archivos de texto. La sintaxis de la función es:
search ARCHIVO,POSICION,MODO
donde:
ARCHIVO es la variable con la que hacemos referencia a un archivo
POSICIÓN Especifica el avance desde la posición que indique el modo
MODO 1-Desde el inicio, 2-Desde la posición actual, 3-Desde el
final.
2.6- Operaciones con recursos del sistema
Una de las grandes ventajas de Perl sobre otros lenguajes es la facilidad con
la que interactúa con otros programas y con el sistema (UNIX en particular pero
cualquiera en lo general). De hecho, una de las grandes mejoras del Perl5 sobre
el Perl4 es que tiene un proceso mucho mejor coordinado de programas
concurrentes invocados desde Perl, por lo que recomiendo ampliamente se
considere el desarrollar programas que usen intensivamente el sistema con Perl5
(o superior).
Por considerar esta solo una introducción planteare solo las formas mas básicas
de interacción con el sistema, recomiendo ampliamente, que para las tareas
especificas para las que se suelen usar llamadas al sistema se consulte la
referencia de Perl en la sección PERLFUNC para averiguar si están presentes y
cual es la manera de usarlas.
En esta sección revisaremos tan solo los métodos de ejecución de programas
comandos del sistema operativo que dividiremos en tres categorías:
-
Como expresiones a evaluar: Ya lo hamos revisado en la sección de
operadores, pero conviene repetirlo en este contexto.
-
Sin interrelación: Invocación de comandos y programas esperando su
culminación antes de terminar, o de modo que sean la ultima acción de
nuestro programa.
-
Entubados: Principalmente mediante el uso de open y close, nos permiten
alimentar de información otros procesos o alimentar nuestro programa del
resultado de ellos.
2.6.1 Como expresiones
Como ya vimos, con el uso de las comillas `` podemos tratar
un comando del S.O. como si fuese una expresión la cual nos regresa los
resultados que dicho comando entrega a su salida estándar.
algunos ejemplos de esto son:
`date`
evalua como "Thu Sep 12 18:49:47 CST 1996\n" suponiendo que esa
fuese la fecha
`wc -l Hola.pl`
evalua como " 3 Hola.pl" suponiendo que Hola.pl tuviese tres líneas.
Es la forma mas sencilla de ejecutar comandos desde nuestro programa en Perl.
Debe aclararse que Perl 4 solo ejecuta el comando dejando la salida estandar de
este a la salida del script de Perl; de modo que no regresa ningun valor a
nuestro script de Perl 4.
2.6.2 Sin interrelación
Por interrelación entiendase que: Ya sea que la entrada estándar del
proceso ejecutado sea tomada desde nuestro programa. O que nuestro programa tome
la salida del comando, de modo que en estos métodos la salida estándar del
programa ejecutado será la salida estándar que nuestro propio programa tiene
(consola por lo regular) y lo mismo para la entrada estándar.
Hay dos métodos para invocar programas con estas características "system"
y "exec", y la diferencia entre estos radica en el
ambiente que el programa invocado ocupara. La sintaxis de estas es:
system(PROGRAMA);
Ejecuta PROGRAMA como un nuevo proceso, espera a que acabe y continua
exec(PROGRAMA);
Ejecuta PROGRAMA, y termina nuestro script siendo reemplazado por el PROGRAMA
que ejecutamos.
donde PROGRAMA es una cadena conde esta el comando (como lo
daríamos en S.O.) a ejecutar, puede ser un comando compuesto, por ejemplo:
$comando="cat Hola.pl |grep daniel";
system($comando);#equivalente a system("cat Hola.pl |grep daniel");
efectivamente escribirá en la salida estándar las líneas del script
Hola.pl en las que aparezca la palabra "daniel".
Cuando un programa se ejecuta, las variables de ambiente, el camino actual y
otras condiciones del sistema operativo suelen ser determinantes para su desempeño,
cuando utilizamos system, el programa a ser ejecutado se ejecuta en un nuevo
ambiente (que no tiene mas relación con el nuestro que gozar de los mismos
privilegios), mientras que cuando lo ejecutamos con exec el programa de hecho
substituye a nuestro script de perl en el ambiente, de modo que hereda todas las
características que nuestro script haya determinado del ambiente.
De este modo, una posible aplicación de exec es cuando debemos forzar que el
programa que ejecutamos tome variables de ambiente que colocamos con el script
de perl, en cambio system es necesario cuando debemos ejecutar mas de un
programa, o cuando debemos realizar mas acciones con nuestro script una vez que
el programa invocado termine.
2.6.3 Entubados
En esta sección, veremos como podemos capturar la salida que los programas
que ejecutemos generan y procesar esta como si fuese un archivo y por otra
parte, como podemos asignar una variable de archivo a la entrada estándar de un
programa que ejecutemos para que procese lo que le alimentemos.
Una nota importante: Cuando ejecutamos programas de este modo Perl no espera
a que el programa termine para continuar, si deseamos que lo haga debemos usar
close con la variable de archivo que les asociemos para indicar que el programa
debe terminar (y perl efectivamente esperara a que lo haga).
La ejecución de comandos o programas asociándolos a archivos se hace añadiendo
el carácter "|" al comando a ejecutar y utilizando el
comando open.
Por ejemplo, supongamos que deseamos procesar el resultado de un programa
llamado "fuente". Si el comando para su ejecución desde sistema
operativo fuese:
fuente saluda 3
deberemos "Abrirlo" de la siguiente forma:
open(RESF,"fuente saluda 3|");
while(<RESF>)
{
#procesar los renglones que va poniendo en $_
}
close(RESF); #espera a que termine y cierra el flujo de datos
Nótese como se coloco el carácter "|" al final
del comando usual. Y que RESF funciona como un archivo abierto
para lectura. También es conveniente recalcar que si deseamos ejecutar un
comando que per se utilice entubamientos se puede hacer, por ejemplo:
open(ALGO,"fuente hola 3|prcesa_hola |");
Asociara a ALGO lo que el programa procesa_hola genere en su
salida estándar, mientras que el S.O. ha entubado la salida de "fuente"
a la entrada de "procesa_hola".
Del mismo modo, si deseamos poner información a la entrada estándar de un
comando deberemos abrirlo colocando el símbolo "|"
como primer carácter del comando. Y el archivo que asociemos se deberá usar
como una archivo abierto para escritura.
De nuevo, cuando indiquemos el close, Perl enviara un EOF al archivo al que
estamos ejecutando y esperara a que este le indique el termino de su proceso.
3- PROGRAMACIÓN ESPECIAL
En la Seccion 2, revisamos la programación básica en Perl, pero la
programación en este lenguaje se basa, sobretodo, en gran cantidad de
optimizaciones y formas alternativas de realizar tareas comunes lo que le dá
una funcionalidad adicional que lo hace en extremo valioso en la realización de
estas tareas o aquellas que las requieran.
Por eso es que dedico toda una sección de este tutorial a tratar estas características
y el modo de emplearlas, para que el nuevo programador, no solo pueda elaborar
programas en Perl, sino que pueda explotarlo para facilitar la realización de
sus tareas mas usuales.
3.1- Uso de Perl en línea, Tareas Comunes
Una de las características de Perl dada su semi naturaleza de interprete es
que puede ser ejecutado y actuar como un interprete de comandos (aunque los
ejecuta hasta el momento de terminar todas las instrucciones con un fin de
archivo desde teclado.
Además, Perl tiene varias opciones en línea que son de extraordinaria
utilidad, por ejemplo, para substituir cadenas a lo largo de todo un archivo, o
indicar que los errores se expresen en un formato mas entendible (en caso de que
no este como default desde que lo compilamos), etc.
Esta sección, se dedica a revisar algunos de estos parámetros de Perl
enfocado a usos específicos.
3.1.1- Formas de Especificar el programa
Existen tres métodos de indicar a Perl cual ha de ser el script a ejecutar:
1- Ejecutando un script directamente que especifique la colocación de Perl
con una línea que inicie con "#!"
2- Pasando el script por la entrada estándar a Perl (completo con todo y fin
de archivo), Pero esta opción no admitirá parámetros para el script.
3- Especificado las instrucciones en línea de comando con el interruptor -e.
Esta la revisaremos con cuidado cuando veamos los interruptores.
3.1.2- Interruptores
En esta sección no intento revisar todos los interruptores descritos en la
sección PERLRUN de la referencia, sino solo algunos de ellos enfocados a
comprender mejor el como se realizan algunas tareas con Perl en línea de
comando, y el orden también ha sido modificado tratando de hacer mas fácil su
comprensión.
-e comando
Este interruptor se usa para indicar una línea de código en Perl a ejecutar
(nota solo una, completa con ";"), si se da este interruptor,
Perl no buscara un nombre de script en la lista de argumentos ,ni la recibirá
por la entrada estándar. Para formar un script de varias líneas, se puede dar
varias veces este interruptor con líneas distintas.
-n
Hace que Perl asuma que existe el ciclo siguiente alrededor del script que le
damos:
while(<>)
{
#aqui va el script
}
-p
Al igual que "-n" hace que Perl asuma un ciclo alrededor
del script, pero además, hace que imprima automáticamente el ultimo valor
evaluado de cada ciclo del script, lo que equivale al siguiente ciclo:
while(<>)
{
#El script va aquí
} continue
{
print;
}
El uso del interruptor -p inhibe al interruptor -n.
-a
Al estar usando un interruptor "-e" o "-p"
hace que los renglones que se van leyendo en el ciclo del "-n"
o "-p" pasen por un "split"
con el delimitador especificado con el interruptor -F y sean
asignados, ya como arreglos a @F, resultando en la siguiente
estructura del while:
while(<>){
@f=split(' ');
#aqui va el script
}
-F
Con este interruptor determina una expresión regular la cual se usara como
delimitado para el split del switch "-a", se puede
poner el patrón entre "/" siendo estos ignorados.
Nota: los interruptores -a y -F son exclusivos
de Perl 5.
-iextencion
Especifica que los archivos procesados por "<>"
sean editados en su lugar, esto es, que el archivo de entrada sea movido a otro
archivo con la extensión especificada, y que en el archivo original se vayan
escribiendo los resultados que generemos. Si no se especifica ninguna extensión,
al final del proceso no habrá ningún archivo con la información original.
-v
Imprime la versión y nivel de parches del ejecutable de Perl.
-w
Imprime advertencias sobre identificadores que se usen solo una vez,
escalares que se usan sin haber recibido valor, subrutinas indefinidas, archivos
sobre los que intentemos de realizar operaciones para las que no fueron
abiertos, etc. Se recomienda para diagnosticas problemas en los programas.
Habiendo revisado algunos interruptores, ahora vamos a ver algunos ejemplos
de las cosas que podemos hacer con ellos.
3.1.3- Tareas Comunes de Perl en Línea de Comando
Entiéndase por Línea de Comando, que desde nuestro interprete de comandos,
invocamos Perl con los parámetros adecuados para que realice una función sin
necesidad de haber escrito un script, por supuesto, no son tareas muy complejas,
pero llegan a ser deslumbrantemente útiles.
3.1.3.1- Reemplazar una cadena en un archivo por otra
Es muy común que, por ejemplo, deseemos reemplazar todas las ocurrencias de,
digamos, un camino y nombre de programa, por otro en un archivo de texto que
podría ser el código fuente de un programa o alguna otra cosa, muchos
procesadores de palabra soportan este tipo de procesos, pero con Perl no se
requieren herramientas extra y podemos trabajar de un solo golpe grandes
cantidades de archivos.
el comando seria de la forma:
Perl -pi.bak -e "s/CADENA1/CADENA2/g;" archivo1 archivo2 archivo3
Recordando lo que significan los interruptores:
-p indica que alrededor de la instrucción tenemos un ciclo
que revisara todas las líneas de todos los archivos que se pasen como parámetros
a script, además, que los resultados se imprimirán (a la salida estándar).
Como este interruptor no recibe valores, puede acompañarse de otro interruptor
(en este caso, "-p -i" es equivalente a "-pi").
-i Los archivos procesados serán editados en línea,
generando respaldos de estos con la extensión .bak, la salida
estándar del bloque de "-p" se redirecciona a los
archivos origianles, de modo que los resultados del script serán el nuevo
contenido de los archivos originales.
-e El comando a ejecutar dentro del ciclo, y cuyo resultado
será el nuevo contenido de los archivos es una substitución (usamos implícitamente
el argumento default $_ para indicar sobre que se hace la
substitución), el comando, es una substitución del patrón en toda la línea
leída.
archivo1 archivo2 archivo3 Son los archivos que usa el ciclo
de "-p" que, como en el caso de cualquier otro script
que usa "<>" son recibidos como parámetros.
El resultado final es que generamos archivo1.bak, archivo2.bak,
archivo3.bak y archivo1, archivo2 y archivo3 y
en los últimos la CADENA1 ha sido reemplazada por CADENA2.
3.1.3.2- Imprimir algún campo de un archivo
En UNIX, gran parte de la información del sistema se almacena en archivos de
texto con separadores para indicar campos, siendo cada renglón un registro,
además, como un ejemplo sencillo, planteemos una ejecución en línea de Perl
que imprima todos los logins del etc/passwd que pertenezcan a un cierto grupo,
digamos, el 100.
Perl -anF: -e 'if($F[3]==100){print "$F[0]\n";}' /etc/passwd |