AnteriorPosterior

Tema 11: Manejo de ficheros (4: Ficheros generales)

  Curso: Curso de Pascal, por Nacho Cabanes

Curso de Pascal. Tema 11: Manejo de ficheros.

Tema 11.4. Manejo de ficheros (4) - Ficheros generales.

Hemos visto cómo acceder a los ficheros de texto y a los fichero "con tipo". Pero en la práctica nos encontramos con muchos ficheros que no son de texto y que tampoco tienen un tipo de datos claro.

Muchos formatos estándar como PCX, DBF o GIF están formados por una cabecera en la que se dan detalles sobre el formato de los datos, y a continuación ya se detallan los datos en sí.

Esto claramente no es un fichero de texto, y tampoco se parece mucho a lo que habíamos llamado ficheros con tipo. Quizás, un fichero "de tipo byte", pero esto resulta muy lento a la hora de leer ficheros de un cierto tamaño. Como suele ocurrir, "debería haber alguna forma mejor de hacerlo..." sonrisa

Pues la hay: declarar un fichero sin tipo, en el que nosotros mismos decidimos qué tipo de datos queremos leer en cada momento.

Ahora leeremos bloques de bytes, y los almacenaremos en un "buffer" (memoria intermedia). Para ello tenemos la orden "BlockRead", cuyo formato es:

procedure BlockRead(var F: Fichero; var Buffer; Cuantos: Word
[; var Resultado: Word]);

donde

  • "F" es un fichero sin tipo (declarado como "var fichero: file" ).
  • "Buffer" es la variable en la que queremos guardar los datos leídos.
  • "Cuantos" es el número de datos que queremos leer.
  • "Resultado" (opcional) almacena un número que indica si ha habido algún error.


Hay otra diferencia con los ficheros que hemos visto hasta ahora, y es que cuando abrimos un fichero sin tipo con "reset", debemos indicar el tamaño de cada dato (normalmente diremos que 1, y así podemos leer variables más o menos grandes indicándolo con el dato "cuantos" que aparece en BlockRead).

Así, abriríamos el fichero con

reset( fichero, 1 );

Los bloques que leemos con "BlockRead" deben tener un tamaño menor de 64K (resultado de multiplicar "cuantos"por el tamaño de cada dato), al menos en Turbo Pascal (quizá alguna versión posterior evite esta limitación).

El significado de "Resultado" es el siguiente: nos indica cuantos datos ha leido realmente. De este modo, si vemos que le hemos dicho que leyera 30 fichas y sólo ha leído 15, hábilmente podremos deducir que hemos llegado al final del fichero guiño. Si no usamos "resultado" y tratamos de leer las 30 fichas, el programa se interrumpirá, dando un error.

Para escribir bloques de datos, utilizaremos "BlockWrite", que tiene el mismo formato que BlockRead, pero esta vez si "resultado" es menor de lo esperado indicará que el disco está lleno.

Esta vez, es en "rewrite" (cuando abrimos el fichero para escritura) donde deberemos indicar el tamaño de los datos (normalmente 1 byte).


Como las cosas se entienden mejor practicando, ahí va un primer ejemplo, tomado de la ayuda en línea de Turbo Pascal y ligeramente retocado, que es un programita que copia un fichero leyendo bloques de 2K:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Copia un fichero      }
 {    COPIAFIC.PAS          }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Turbo Pascal 7.0    }
 {    - Free Pascal 2.0.2   }
 {--------------------------}

 program CopiaFichero;
 { Sencillo y rápido programa de copia de ficheros, SIN comprobación
   de errores }

 var
   Origen, Destino: file;
   CantLeida, CantEscrita: Word;
   NombreOrg, NombreDest: String;
   Buffer: array[1..2048] of Char;

 begin
   Write( 'Introduzca el nombre del fichero ORIGEN... ' );
   ReadLn( NombreOrg );
   Write( 'Introduzca el nombre del fichero DESTINO... ' );
   ReadLn( NombreDest );
   Assign( Origen, NombreOrg );
   Reset( Origen, 1 );                                { Tamaño = 1 }
   Assign( Destino, NombreDest );
   Rewrite( Destino, 1 );                             { Lo mismo }
   WriteLn( 'Copiando ', FileSize(Origen), ' bytes...' );
   repeat
     BlockRead( Origen, Buffer, SizeOf(Buffer), CantLeida);
     BlockWrite( Destino, Buffer, CantLeida, CantEscrita);
   until (CantLeida = 0) or (CantEscrita <> CantLeida);
   Close( Origen );
   Close( Destino );
   WriteLn( 'Terminado.' )
 end. 

Un único comentario: es habitual usar "SizeOf" para calcular el tamaño de una variable, en vez de calcularlo a mano y escribir, por ejemplo, 2048. Es más fiable y permite modificar el tipo o el tamaño de la variable en la que almacenamos los datos leídos sin que eso repercuta en el resto del programa.


Aplicación a un fichero GIF.

Un segundo ejemplo, que muestra parte de la información contenida en la cabecera de un fichero GIF, leyendolo con la ayuda de un "record":

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Lee la cabecera de    }
 {    un fichero GIF        }
 {    GIFHEAD.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {--------------------------}

 program GifHeader;

 Type
   Gif_Header = Record                 { Primeros 13 Bytes de un Gif }
     Firma, NumVer     : Array[1..3] of Char;
     Tam_X,
     Tam_Y             : Word;
     _Packed,
     Fondo,
     Aspecto           : Byte;
   end;

 Var
   Fich : File;
   Cabecera : GIF_Header;
   Nombre: String;

 begin
   Write( '¿Nombre del fichero GIF (con extensión)? ');
   ReadLn( Nombre );
   Assign( Fich, Nombre );
   Reset( Fich, 1 );                           { Tamaño base: 1 byte }
   BlockRead( Fich, Cabecera, SizeOf(Cabecera) );
   Close( Fich );
   With Cabecera DO
   begin
     WriteLn('Version: ', Firma, NumVer);
     WriteLn('Resolución: ', Tam_X, 'x',
       Tam_Y, 'x', 2 SHL (_Packed and 7));
   end;
 end. 

Como último ejercicio sobre ficheros queda algo que asusta, pero que no es difícil: un programa que deberá preguntarnos el nombre de un fichero, y nos lo mostrará en la pantalla página por página. En la parte izquierda de la pantalla deberán aparecer los bytes en hexadecimal, y en la parte derecha como letras.


Abrir exclusivamente para lectura.

Antes de dar casi por terminado el tema de ficheros, hay algo que nos puede sacad de algún apuro...

Puede interesarnos leer un fichero que se encuentra en un CdRom, o en una red de ordenadores. Normalmente, tanto los CdRom como las "unidades de red" se comportan de forma muy similar a un disco duro "local", de modo que no supondrá ningún problema acceder a su contenido desde MsDos y desde Windows. En cambio, si intentamos leer datos desde un CdRom o desde una unidad de red, con Turbo Pascal 7, de la misma manera que hemos hecho hasta ahora, nos encontraremos con que no podemos, sino que obtenemos un error de "File acces denied" (denegado el acceso al fichero).

El motivo es que Turbo Pascal intenta abrir el fichero tanto para leer de él como para escribir en él. Esto es algo útil si utilizamos nuestro disco duro o un diskette, pero normalmente no tendremos la posibilidad de grabar directamente datos en un CdRom convencional, ni tendremos a veces permisos para escribir en una unidad de red.

Pero podemos cambiar esta forma de comportarse de Turbo Pascal. Lo haremos mediante la variable FileMode, que está definida siempre en Turbo Pascal (está en la unidad System). Esta variable normalmente tiene el valor 2 (abrir para leer y escribir), pero también le podemos dar los valores 1 (abrir sólo para escribir) y 0 (abrir sólo para lectura).

Por tanto, la forma de abrir un fichero que se encuentre en un CdRom o en una unidad de red sería añadir la línea

FileMode := 0;

antes de intentar abrir el fichero con "reset".

Esta misma idea se puede aplicar también con Free Pascal.

Actualizado el: 06-08-2006 12:41

AnteriorPosterior