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
2010-03-03
nmutar entre los dos keymaps tecleando

CTRL-^.
o bien
CTRL-6.

  • Compruebe con que codificación está trabajando vim:
    :set encoding
      encoding=utf-8
    
    Es posible cambiar la codificación con la que se está editando:
    :set encoding latin1
    
    Esto no modifica la codificación del fichero.
  • Para saber mas sobre como utilizar Unicode y keymaps en vim lea las ayudas sobre los tópicos:
  • Apertura de ficheros UTF-8

    Use la forma con tres argumentos de open y especifique la capa :utf8 para que la entrada/salida a ese fichero se procesada por dicha capa. Por ejemplo:

    lhp@nereida:~/Lperl/src/testing$ cat -n abreutf8.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  binmode(STDOUT, "utf8");
     4  open my $f, '<:utf8', shift();
     5  my @a = <$f>;
     6  chomp(@a);
     7  print "$_ tiene longitud ".length($_)."\n" for @a;
    

    Al ejecutar produce una salida como esta:

    lhp@nereida:~/Lperl/src/testing$ abreutf8.pl tutu
    ジジェッニgfは大好あき tiene longitud 14
    αβγεφγη tiene longitud 7
    νμοπ;ρ^αβψδε tiene longitud 12
    & ασηφδξδξδη tiene longitud 12
    abc tiene longitud 3
    αβγδ&αβψ tiene longitud 8
    

    El Módulo charnames

    El módulo charnames facilita la introducción de caracteres unicode:

    lhp@nereida:~/Lperl/src/testing$ cat -n alfabeta.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use charnames qw{:full greek hebrew katakana};
     4  binmode(STDOUT, ':utf8');
     5
     6  print "\N{alpha}+\N{beta} = \N{pi}\n";
     7  print "\N{alef} es la primera letra del alfabeto hebreo\n";
     8  print "Un poco de Katakana: \N{sa}\N{i}\N{n}\N{mo}\n";
     9
    10  # Usando el nombre completo definido en el Standard Unicode
    11  print "Hello \N{WHITE SMILING FACE}\n";
    
    Cuando se ejecuta produce una salida como:
    lhp@nereida:~/Lperl/src/testing$ alfabeta.pl
    α+β = ピ
    א es la primera letra del alfabeto hebreo
    Un poco de Katakana: サインモ
    Hello ☺
    
    Obsérvese como la salida para \N{pi} no muestra la letra griega π sino el correspondiente símbolo Katakana : atención a las colisiones entre alfabetos.

    Las funciones viacode y vianame son recíprocas y nos dan la relación nombre-código de un carácter:

    pl@nereida:~/Lperltesting$ perl5.10.1 -COE -Mutf8 -dE 0
    main::(-e:1):   0
      DB<1>   use charnames ':full'
      DB<2>  print charnames::viacode(0x2722)
    FOUR TEARDROP-SPOKED ASTERISK
      DB<3> printf "%04X", charnames::vianame("FOUR TEARDROP-SPOKED ASTERISK")
    2722
    

    Expresiones Regulares y Unicode

    Usando utf8 es posible usar operadores como tr y expresiones regulares sobre cadenas UTF-8:

    lhp@nereida:~/Lperl/src/testing$ cat -n useutf8.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use utf8;
     4  binmode(STDOUT, ':utf8');
     5
     6  my $x = 'áéíóúñ€';
     7  print "$x\n";
     8  print length($x)."\n";
     9
    10  my$y = $x;
    11  $y =~ tr/áéíóúñ€/aeioun$/;
    12  print "$y\n";
    13
    14  $y = $x;
    15  $y =~ m/áéíóúñ(€)/;
    16  print "$1\n";
    
    Al ejecutar, este programa produce la salida:
    lhp@nereida:~/Lperl/src/testing$ useutf8.pl
    áéíóúñ€
    7
    aeioun$
    €
    

    Macros: Dígitos y Words

    Macros como \d han sido generalizadas. Los digitos Devanagari tienen códigos del 2406 (0x966) al 2415 (0x96F):

    lhp@nereida:~/Lperl/src/testing$ unicode -x 966..96f | egrep '096|\.0'
              .0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .A .B .C .D .E .F
         096.  ॠ  ॡ      ।  ॥  ०  १  २  ३  ४  ५  ६  ७  ८  ९
    
    El siguiente ejemplo muestra que expresiones regulares como \d+ reconocen los digitos Devanagari:
    lhp@nereida:~/Lperl/src/testing$ cat -n regexputf8.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  binmode(STDOUT, "utf8");
     4  use utf8;
     5
     6  # Digitos Devanagari del 0 al 9
     7  my @dd  = map { chr } (2406..2415);
     8  my $x = join '+', @dd;
     9  print "La interpolación ocurre: $x\n";
    10  my @number = $x =~ m{(\d+)}g;
    11  print "Las expresiones regulares funcionan: @number\n";
    12  print "Sin embargo la conversión numérica no es automática: ".($number[0]+$number[1])."\n";
    

    Como se indica en el programa la conversión automática de dígitos en otros juegos de caracteres no funciona. Véase la ejecución:

    lhp@nereida:~/Lperl/src/testing$ regexputf8.pl
    La interpolación ocurre: ०+१+२+३+४+५+६+७+८+९
    Las expresiones regulares funcionan: ० १ २ ३ ४ ५ ६ ७ ८ ९
    Argument "\x{967}" isn't numeric in addition (+) at ./regexputf8.pl line 12.
    Argument "\x{966}" isn't numeric in addition (+) at ./regexputf8.pl line 12.
    Sin embargo la conversión numérica no es automática: 0
    

    Lo mismo ocurre con la macro\w:

    lhp@nereida:~/Lperl/src/testing$ cat -n words_utf8.pl
         1  #!/usr/local/bin/perl -w
         2  use strict;
         3  use utf8;
         4  use charnames qw{greek};
         5  binmode(STDOUT, ':utf8');
         6
         7  my $x = 'áéíóúñ€αβγδη';
         8  my @w = $x =~ /(\w)/g;
         9  print "@w\n";
    lhp@nereida:~/Lperl/src/testing$ words_utf8.pl
    á é í ó ú ñ α β γ δ η
    

    Semántica de Carácter versus Semántica de Byte

    Cuando se procesan datos codificados en UTF-8 el punto casa con un carácter UTF-8. La macro \C puede ser utilizada para casar un byte:

    pl@nereida:~/Lperltesting$ cat -n dot_utf8.pl
         1  #!/usr/local/lib/perl/5.10.1/bin/perl5.10.1 -w -COE
         2  use v5.10;
         3  use strict;
         4  use utf8;
         5
         6  my $x = 'αβγδεφ';
         7  my @w = $x =~ /(.)/g;
         8  say "@w";
         9
        10  my @v = map { ord } $x =~ /(\C)/g;
        11  say "@v";
    pl@nereida:~/Lperltesting$ ./dot_utf8.pl
    α β γ δ ε φ
    206 177 206 178 206 179 206 180 206 181 207 134
    

    El mismo efecto de \C puede lograrse mediante el pragma use bytes el cual cambia la semántica de caracteres a bytes:

    lhp@nereida:~/Lperl/src/testing$ cat -n dot_utf8_2.pl
         1  #!/usr/local/bin/perl -w
         2  use strict;
         3  use utf8;
         4  use charnames qw{greek};
         5
         6  binmode(STDOUT, ':utf8');
         7
         8  my $x = 'αβγδεφ';
         9
        10  my @w = $x =~ /(.)/g;
        11  print "@w\n";
        12
        13  {
        14    use bytes;
        15    my @v = map { ord } $x =~ /(.)/g;
        16    print "@v\n";
        17  }
    

    Caja e Inversión de Cadenas Unicode

    El siguiente ejemplo ilustra el uso de las funciones de cambio de caja (tales como uc , lc , lcfirst y ucfirst ) asi como el uso de reverse con cadenas unicode:

    lhp@nereida:~/Lperl/src/testing$ cat -n alfabeta1.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use utf8;
     4  use charnames qw{greek};
     5  binmode(STDOUT, ':utf8');
     6
     7  my $x = "\N{alpha}+\N{beta} = \N{pi}";
     8  print uc($x)."\n";
     9  print scalar(reverse($x))."\n";
    10
    11  my $y = "áéíóúñ";
    12  print uc($y)."\n";
    13  print scalar(reverse($y))."\n";
    
    Al ejecutarse, el programa produce la salida:

    lhp@nereida:~/Lperl/src/testing$ alfabeta1.pl
    Α+Β = Π
    π = β+α
    ÁÉÍÓÚÑ
    ñúóíéá
    

    Propiedades

    El estandar Unicode declara que cadenas particulares de caracteres pueden tener propiedades particulares y que una expresión regular puede casar sobre esas propiedades utilizando la notación \p{...}:

    lhp@nereida:~/Lperl/src/testing$ cat -n properties.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use utf8;
     4  use charnames qw{greek};
     5  binmode(STDOUT, ':utf8');
     6
     7  my @a = ('$', 'az', '£', 'α', '€', '¥');
     8  my $x =  "@a\n";
     9
    10  print /\p{CurrencySymbol}/? "$_ = Dinero!!\n" : "$_ : No hay dinero\n" for @a;
    11  print /\p{Greek}/? "$_ = Griego\n" : "$_ : No es griego\n" for @a;
    
    Al ejecutar este script obtenemos:

    lhp@nereida:~/Lperl/src/testing$ properties.pl
    $ = Dinero!!
    az : No hay dinero
    £ = Dinero!!
    α : No hay dinero
    € = Dinero!!
    ¥ = Dinero!!
    $ : No es griego
    az : No es griego
    £ : No es griego
    α = Griego
    € : No es griego
    ¥ : No es griego
    

    El módulo Unicode::Properties permite obtener las propiedades de un carácter:

    casiano@millo:~$ echo $PERL5LIB
    /soft/perl5lib/perl5_10_1/lib/:/soft/perl5lib/perl5_10_1/lib/perl5:/soft/perl5lib/perl5_10_1/share/perl/5.8.8/
    casiano@millo:~$ perl5.10.1 -COE -Mutf8 -dE 0
    main::(-e:1):   0
      DB<1> use Unicode::Properties 'uniprops'
      DB<2> x  uniprops ('☺'); # Unicode smiley face
    0  'Alphabetic'
    1  'Any'
    2  'Assigned'
    3  'IDContinue'
    4  'IDStart'
    5  'InLatin1Supplement'
    6  'Latin'
    7  'Lowercase'
    

    Conversores

    Hay un buen número de utilidades de conversión

    Véase también



    Subsecciones
    Casiano Rodríguez León
    2011-02-09