Operaciones sobre Ficheros

En Perl un filehandle es el nombre una conexión de entrada/salida que conecta nuestro proceso Perl con el mundo exterior. Esto es, se trata del nombre de una conexión, no necesariamente del nombre de un fichero. Existen seis filehandles que son especiales: STDIN, STDOUT, STDERR, DATA, ARGV y ARGVOUT.

Abrir un fichero

Para abrir una conexión se utiliza el operador open. Por ejemplo:

if (open FILE, "alus.txt"   ) { ... # Para lectura   }
if (open FILE, "<alus.txt"  ) { ... # Para lectura   }
if (open FILE, ">alus.txt"  ) { ... # Para escritura }
if (open FILE, ">>alus.txt" ) { ... # Para añadir    }

El operador open devuelve verdadero o falso, dependiendo de si la operación pudo realizarse con éxito o no.

Se puede usar una expresión escalar en lugar del especificador de fichero. Por ejemplo:

my  $outfile = "alu.txt";
open FILE, "> $outfile";

Observe el espacio después del ``mayor que''. Asi se evitan extrañas conductas, si por ejemplo $outfile es algo asi como >alu.txt, se podriá producir un append (esto es >>) en vez de una escritura (>).

Cerrar un fichero

Para cerrar el fichero use el operador close:

close FILE;

Perl cierra automáticamente un fichero cuando se reabre o bien cuando termina la ejecución del programa.

Errores al abrir un fichero

La típica frase Perl para abrir un fichero es:

open FILE, ">>logfile" or die "No se puede crear el fichero: $!";
Esta expresión aprovecha la evaluación en circuito corto. La función die imprime el mensaje a STDERR y hace que nuestro programa termine con un estatus distinto de cero. Además añade al mensaje el nombre del programa y el número de línea en caso de que el mensaje de error no termine en un retorno de carro (\n). La variable especial $! contiene el último mensaje de error del sistema operativo.

Lectura desde un fichero

Una vez que un fichero esta abierto para lectura, podemos leer las líneas de la misma forma que lo hacemos desde STDIN usando el operador de lectura <FILEHANDLE>. Veamos un ejemplo:

my $user = shift;
open PASSWD, "/etc/passwd" or die "Se esperaba un sistema Unix. $!";
while (<PASSWD>) {
  chomp;
  if (/^$user:/) { print "$_\n"; }
}

El separador de lectura

La variable especial $/ contiene el separador de lectura, que por defecto es un \n. Asi, si le asignamos $/ = '.'; en la siguiente lectura se leerá hasta el próximo punto o hasta el final del fichero si no lo hubiera. Asignarle a $/ la cadena vacía "" hace que se lea hasta el siguiente párrafo (esto es, hasta la siguiente aparición de dos o más líneas en blanco). Cuando tiene el valor undef se leerá todo el resto del fichero.

undef $/;
$x = <FILE>; # Ahora $x contiene todo el fichero

Ejercicio 2.5.1   Muerte Prematura:

Considere el siguiente código:

open FILEHANDLE, shift;
while (<FILEHANDLE>) {
  print;
}
¿Existe el riesgo de ``muerte'' prematura debido a que una línea contenga sólamente "0"? Pruebe con varios posibles ficheros de entrada. Observe que el operador de lectura <FILEHANDLE> incluye el retorno de carro \n en \$_.

El operador select

El operador select modifica la salida por defecto. En vez de STDOUT el fichero especificado será utilizado por defecto. La variable especial $|, cuando vale 1, hace que los buffers de salida se vacíen inmediatamente a la salida por defecto.

> cat select.pl
#!/usr/bin/perl -w

my $user = shift;
open LOG, ">/tmp/log.file" or die "Se esperaba un sistema Unix";
select LOG;
$| = 1;
print "Esto es una prueba\n";
select STDOUT;
print "Esto es otra prueba\n"

Veamos una ejecución:

nereida:~/perl/src> select.pl
Esto es otra prueba
nereida:~/perl/src> cat /tmp/log.file
Esto es una prueba

El separador de campos de salida

La variable $, contiene el separador para el operador print. Normalmente no hay separación entre los argumentos de print. Esto puede modificarse como muestra el ejemplo:

print 1,2,3 # Salida: 123
$, = ',';
print 1,2,3 # Salida: 1,2,3
$, = ';';
print 1,2,3 # Salida: 1;2;3

El separador de registros de salida

Perl normalmente no separa dos salidas realizadas en dos llamadas consecutivas a la sentencia print. Para ello se puede usar la variable $\. Vea el ejemplo:

$\ = "\n***\n";
print "uno"; print "dos";
La salida será:
uno
***
dos
***

Reapertura de uno de los ficheros estandar

Si reabrimos un fichero, el viejo será cerrado automáticamente. Si se trata de uno de los tres ficheros estándar y falla la reapertura se restaura el original. Así para redirigir los errores escribiríamos:

open STDERR, ">>/home/casiano/.error.log" 
  or die "No se pudo abrir fichero de errores: $!";

Tests sobre ficheros

Perl utiliza -e $filevar para comprobar la existencia de un fichero cuyo nombre es el guardado en la variable $filevar. Si el fichero existe el resultados es verdadero; en otro caso es falso. Por ejemplo:

$name = "index.html";
if (-e $name) {
    print "Ya existe un fichero denominado $name\n";
} else {
    print "No existe un fichero denominado $name\n";
}

He aqui otro ejemplo:

if (-e "index.html" && -e "index.cgi") {
    print "Encontrados.\n";
}

Existen otros operadores. Por ejemplo, -r $filevar es cierto si el fichero cuyo nombre se guarda en $filevar existe y es de lectura. Análogamente, -w $filevar comprueba si es de escritura.

print "Donde? ";
$filename = <STDIN>;
chomp $filename; 
if (-r $filename && -w $filename) {
        # El fichero existe y es de lectura y escritura 
        ...
}

La tabla 2.1 contiene algunos de los operadores mas importantes.


Tabla 2.1: Operadores de fichero y su significado
Fichero Significado
-r El fichero o directorio es legible
-w El fichero o directorio es de escritura
-e El fichero o directorio existe
-x El fichero es ejecutable
-z El fichero existe y tiene tamaño cero (Los directorios nunca tiene tamaño cero)
-s El fichero o directorio existe y tiene tamaño no nulo (el tamaño en bytes
-f La entrada es un fichero
-d La entrada es un directorio
-t isatty sobre el fichero es cierto (esto es, es un dispositivo de caracteres)
-T Fichero de texto
-B Fichero binario
-M Edad de modificación en días
-A Tiempo de acceso en días
-C Edad de modificación del Inode en días


Estos operadores pueden usarse indistintamente sobre filehandles o nombres de fichero. Por ejemplo:

if (-x SOMEFILE) {
        # SOMEFILE es ejecutable
}

Si no se especifica el nombre del fichero, el operador por defecto es la variable $_. Por ejemplo:

foreach (@some_list_of_filenames) {
        print "$_ es de lectura\n" if -r; # Lo mismo que -r $_
}

La función stat

Si se quiere obtener información más detallada como el número de enlaces a un fichero o el uid del propietario de un fichero es necesario recurrir a la función stat. El operando de stat es un fichero o un nombre de fichero. La función stat devuelve bien una lista vacía (en caso de error) o una lista de 13 elementos (véase la tabla 2.2).


Tabla 2.2: Valores devueltos por stat
0 dev device
1 ino inode
2 mode permisos
3 nlink numero de hard links
4 uid user ID del propietario
5 gid group ID del propietario
6 rdev tipo de dispositivo (ficheros especiales)
7 size tamaño total, en bytes
8 atime Tiempo del último acceso
9 mtime Tiempo de la última modificación
10 ctime Tiempo del último cambio del inodo
11 blksize blocksize para filesystem I/O
12 blocks número de bloques asignados


Los argumentos atime, mtime y ctime indican cuantos segundos han pasado desde el comienzo de 1970 MUT (Midnight Universal Time).

En el siguiente ejemplo, en la línea 1 el uso del operador glob nos permite la expansión de los comodines tipo shell:

  DB<1> @f = glob('xml*')
  DB<2> p "@f"
xml xmlparser.pl xmlparserinput.xml
  DB<3> @a = stat "xmlparser.pl"
  DB<4> x @a
0  2051
1  2171300
2  33261
3  1
4  1007
5  1007
6  0
7  191
8  1112287076
9  1087853581
10  1099385099
11  4096
12  8

La función localtime permite convertir números en formato MUT en una cadena tipo fecha:

  DB<1> @a = stat "xmlparser.pl"
  DB<2> use constant mtime => 9
  DB<3> x $a[mtime]
0  1087853581
  DB<4> p scalar(localtime $a[mtime])
Mon Jun 21 22:33:01 2004

Cuando se llama a stat sobre un enlace simbólico, stat devuelve información sobre el fichero apuntado, no sobre el enlace. Si se trata de un enlace en vez de un fichero, use la función lstat. Si el operando no es un enlace simbólico lstat devuelve la misma información que stat. Cuando no se indica operando, ambos stat y lstat usan la variable por defecto $_.

La Función openhandle

La función openhandle en Scalar::Util permite saber si una expresión es un manejador de fichero (o un atado mediante tie de un manejador).

     openhandle FH
Retorna FH si FH puede ser usado como manejador y esta abierto. En otro caso retorna undef:

 $fh = openhandle(*STDIN);           # \*STDIN
 $fh = openhandle(\*STDIN);          # \*STDIN
 $fh = openhandle(*NOTOPEN);         # undef
 $fh = openhandle("scalar");         # undef



Subsecciones
Casiano Rodríguez León
2009-10-04