Ficheros Binarios

La función read

La función read FILEHANDLE,SCALAR,LENGTH,OFFSET lee desde el fichero FILEHANDLE un número de LENGTH caracteres en la variable SCALAR. Devuelve el número de caracteres leídos, 0 si es el final de fichero y undef si hubo un error. Es posible indicar que el fichero usado es binario mediante la función binmode (línea 7 en el ejemplo que sigue):
lhp@nereida:~/Lperl/src$ cat -n binaryfiles.pl
 1  #!/usr/bin/perl -w
 2  use strict;
 3  my $buffer = "";
 4  my $file = shift;
 5
 6  open(FILE, "< $file");
 7  binmode(FILE);
 8  my $s = -s $file;
 9  read(FILE, $buffer, $s);
10  close(FILE);
11
12  open(FILE, "> $file.sal");
13  syswrite(FILE, $buffer, $s);
14  close(FILE);
15
16  my $c;
17  foreach (split(//, $buffer)) {
18    printf("%02x ", ord($_));
19    print "\n" unless ++$c % 20;
20  }
21  print "\n";
La salida de la ejecución nos permite comprobar que la copia de un ejecutable a.out preserva el formato binario:

lhp@nereida:~/Lperl/src$ ./binaryfiles.pl a.out | head -3
7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 02 00 03 00
01 00 00 00 e0 82 04 08 34 00 00 00 f4 0d 00 00 00 00 00 00
34 00 20 00 07 00 28 00 22 00 1f 00 06 00 00 00 34 00 00 00
lhp@nereida:~/Lperl/src$ ls -ltr | tail -2
-rwxr-xr-x   1 lhp lhp   341 2006-06-21 18:00 binaryfiles.pl
-rw-r--r--   1 lhp lhp  6969 2006-06-21 18:02 a.out.sal
lhp@nereida:~/Lperl/src$ chmod a+x a.out.sal; ./a.out.sal
hello world!
lhp@nereida:~/Lperl/src$ ./a.out
hello world!
lhp@nereida:~/Lperl/src$

La función syswrite

La función syswrite (línea 13) tiene el siguiente formato:

     syswrite FILEHANDLE,SCALAR,LENGTH,OFFSET
     syswrite FILEHANDLE,SCALAR,LENGTH
     syswrite FILEHANDLE,SCALAR

Intenta escribir LENGTH bytes de SCALAR en FILEHANDLE saltándose los buffers de E/S. Si no se especifica LENGTH se escribe todo SCALAR.



Subsecciones
Casiano Rodríguez León
2010-03-03
textbf">shell. Asi en una expresión como:
my @all_files = glob "*";
la variable all_files guarda ordenados alfabéticamente la lista de los nombres de los ficheros en el directorio actual.

Es posible, como indica el siguiente ejemplo utilizar varios patrones separados por espacios.

my @including_hidden = glob ".* *";

Usar glob es mas eficiente y mas portable que enviar el requerimiento a la shell del sistema usando backticks (como por ejemplo en `ls .*`).

Lectura desde Globs

Cuando en el operador de lectura <> el argumento es una cadena glob de descripción de ficheros con comodines, el globbing se produce automáticamente:

my $dir = "/home/casiano/";
my $dir_files = <$dir/* $dir/.*>;

También puede hacerse en un contexto de lista:

DB<1> @modules = <*.pm>
DB<2> p "@modules"
A.pm Abstract.pm B.pm C.pm DandC.pm Frac.pm Fraction.pm

En general, si el identificador entre ángulos es un filehandler, Perl hace un acceso a través de él, en caso contrario interpreta que se trata de una operación de globbing. Esto es asi, incluso si la lectura hace uso de un manipulador indirecto que use variables intermedias. Véase el siguiente ejemplo:

  DB<1> $f = 'yapp'
  DB<2> @f = <$f/*.yp>
  DB<3> p "@f"
yapp/aSb.yp yapp/Autoaction1.yp yapp/Calc.yp yapp/Calc2.yp 
  DB<4> open F, 'logic.pl'
  DB<5> $x = F
  DB<6> @x = <$x>
  DB<7> p "@x"
#!/usr/bin/perl -w
 $a = 4; $b = "hola"; $c = 0; $d = "";
 print $a && $b,"\n";
 print $a and $b,"\n";
 print ($a and $b),"\n";

Lectura de Directorios en Contexto de Lista

lhp@nereida:~/alu/0607$ perl -wde 0
main::(-e:1):   0
  DB<1> use List::Util qw(sum)
  DB<2> p sum(map { -s $_ } <alu*>)
163840
En este código, la lectura <alu*> se produce en un contexto de lista. Por ello el directorio completo es leído y produce una lista con todos los nombres de ficheros y directorios alu* existentes en el directorio actual. El subsecuente map produce los tamaños y la llamada a sum devuelve el tamaño total.

Contexto Booleano: Bucles con Lecturas de Directorios

En ocasiones es importante distinguir entre falsedad e indefinición, como en este ejemplo:

while ($file = <*>) {
  do_something($file);
}
En este código, en cada pasada del bucle el operador <*> produce un nuevo nombre de fichero del directorio actual. El nombre es asignado a $file. ¿Que ocurre si el nombre del fichero es "0"?. En tal caso el bucle debería tener una ''muerte'' prematura. La codificación correcta sería:
while (defined($file = <*>) {
   do_something($file);
}

Siguiendo la filosofía de ''ayudar'' al programador, las últimas versiones de Perl tiene el comportamiento ''esperado''. Esto es, detectan el uso en un while del glob e interpretan que "0" como nombre de fichero no es falso. Sin embargo en otro contexto, "0" si se interpreta como falso. Observe el siguiente ejemplo:

$ cat muerte_prematura2.pl
#!/usr/bin/perl -w

my $file;
my @a = ("uno","0","dos");

while ($file = shift @a) {
  do_something($file);
}

sub do_something {
  print "$file\n";
}
Al ejecutar obtenemos:
$ ./muerte_prematura2.pl
uno

Esta modificación de la evaluación ''normal'' de una expresión en un contexto lógico/booleano no es (del todo) privilegio exclusivo de Perl. El programador puede conseguir un efecto similar usando el módulo Contextual::Return de Damian Conway, el cual permite crear variables contextuales multitipo con mas de dos tipos:

lhp@nereida:~/Lperl/src/testing$ cat -n context1.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  use Contextual::Return;
 4
 5  my $x = BOOL { 0 } NUM { 3.14 } STR { "pi" };
 6
 7  unless ($x) { warn "¡El famoso número $x (".(0+$x).") pasa a ser falso!\n" } # executed!
lhp@nereida:~/Lperl/src/testing$ context1.pl
¡El famoso número pi (3.14) pasa a ser falso!



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