30 agosto 2007

Trabajando con archivos de texto y binarios (III)

Vamos a ver las distintas maneras que tenemos de manejar archivos binarios.

CREANDO UN ARCHIVO BINARIO

El crear un archivo binario utilizando AssignFile no es muy diferente de crear un archivo de texto:

procedure TFPrincipal.CrearArchivoBinario;
var
F: File of byte;
i: Integer;
begin
AssignFile( F, ExtractFilePath( Application.ExeName ) + 'prueba.dat' );
Rewrite( F );

for i := 1 to 10 do
Write( F, i );

CloseFile( F );
end;

Como se puede apreciar, la variable F representa un puntero a un tipo de archivo de bytes. Después utilizamos la función Write para ir guardando byte a byte dentro del archivo.

Cuando los archivos binarios son pequeños no hay problema pero cuando son grandes la lentitud puede ser insoportable (parece como si se hubiera colgado el programa). Cuando ocurre esto entonces hay que crear un buffer temporal para ir guardando los bytes en bloques de 1 Kb, 10 Kb, 20 Kb, etc. Hoy en día todo lo que entra y sale de un disco duro para por la memoria caché del mismo (a parte de la memoria virtual de Windows).

Vamos a ver un ejemplo de como crear un archivo binario grande (100 Kb) utilizando un buffer. Vamos a guardar en el archivo bytes consecutivos (0, 1, 2, .. 255, 0, 1, 2, ...):

procedure TFPrincipal.CrearArchivoBinarioConBuffer;
var
F: File of byte;
i, j: Integer;
b: Byte;
Buffer: array[1..1024] of byte;
begin
AssignFile( F, ExtractFilePath( Application.ExeName ) + 'prueba.dat' );
Rewrite( F );

b := 0;
// Guardamos 100 veces el buffer de 1 KB (100 KB)
for j := 1 to 100 do
begin
for i := 1 to 1024 do
begin
Buffer[i] := b;
Inc( b );
end;

BlockWrite( F, Buffer, 1024 );
end;

CloseFile( F );
end;

Hemos creado un buffer de 1024 bytes para guardar la información mediante el procedimiento BlockWrite que toma como primer parámetro el puntero F, como segundo parámetro el buffer del cual va a guardar la información y como tercer parámetro la longitud del buffer. Cuando más grande sea nuestro buffer menos accesos a disco se necesita y más suelto va nuestro programa.

Ahora veremos como hacer lo mismo utilizando la clase TFileStream.

CREANDO UN ARCHIVO BINARIO CON LA CLASE TFILESTREAM

El parecido con la rutina anterior es casi idéntico salvo que hemos sustituido un fichero de tipo File of byte por la clase TFileStream. El método Write toma como parámetros el buffer y su longitud:

procedure TFPrincipal.CrearStreamBinario;
var F: TFileStream;
Buffer: array[0..1023] of byte;
i, j: Integer;
b: Byte;
begin
F := TFileStream.Create( ExtractFilePath( Application.ExeName ) + 'prueba.dat', fmCreate );

b := 0;
// Guardamos 100 veces el buffer de 1 KB (100 KB)
for j := 1 to 100 do
begin
for i := 0 to 1023 do
begin
Buffer[i] := b;
Inc( b );
end;

F.Write( Buffer, 1024 );
end;

F.Free;
end;

MODIFICANDO UN ARCHIVO BINARIO

En un archivo que no sea de tipo TextFile no se puede utilizar el procedimiento Append para añadir datos al final del mismo. Usando AssignFile se pueden utilizar dos métodos:

- Leer la información de todo el archivo en un buffer, añadir información al final del mismo y posteriormente guardarlo todo sobrescribiendo el archivo con Rewrite.

- Otro método sería crear una copia del archivo y añadirle datos al final del mismo. Luego habría que borrar el original y sustituirlo por este nuevo.

Ambos métodos no los voy a mostrar ya que sería algo primitivo en los tiempos que estamos. Para ello nada mejor que la clase TFileStream para tratamiento de archivos binarios.

MODIFICANDO UN ARCHIVO BINARIO UTILIZANDO LA CLASE TFILESTREAM

Añadir datos a un archivo binario utilizando la clase TFileStream es tan fácil como irse al final del archivo y ponerse a escribir. De hecho sólo hemos añadido una línea de código al archivo anterior y hemos cambiado el método de apertura:

procedure TFPrincipal.AnadirStreamBinario;
var F: TFileStream;
Buffer: array[0..1023] of byte;
i, j: Integer;
b: Byte;
begin
F := TFileStream.Create( ExtractFilePath( Application.ExeName ) + 'prueba.dat', fmOpenWrite );
F.Position := F.Size;

b := 0;
// Guardamos 100 veces el buffer de 1 KB (100 KB)
for j := 1 to 100 do
begin
for i := 0 to 1023 do
begin
Buffer[i] := b;
Inc( b );
end;

F.Write( Buffer, 1024 );
end;

F.Free;
end;


LEYENDO UN ARCHIVO BINARIO

Para la lectura de un archivo binario también utilizaremos un buffer para acelerar el proceso:

procedure TFPrincipal.CargarStreamBinario;
var F: TFileStream;
Buffer: array[0..1023] of byte;
begin
F := TFileStream.Create( ExtractFilePath( Application.ExeName ) + 'prueba.dat', fmOpenRead );

// ¿No ha llegado al final de archivo?
while F.Position < F.Size do
begin
// Leemos un bloque de 1024 bytes
F.Read( Buffer, 1024 );
// ya tenemos un bloque de información en el buffer
// podemos hacer lo que queramos con el antes de cargar el siguiente bloque
end;

F.Free;
end;

¿Cómo sabemos cuando se termina el archivo? Lo sabemos por la variable Position que se va moviendo automáticamente a través del método Read. Cuando llegue al final (F.Size) cerramos el archivo.

Con esto llegamos a la conclusión de que para el manejo de archivos de texto lo ideal es utilizar AssignFile, StringList o campos Memo, pero para archivos binarios TFileStream cumple mejor su función.

En el próximo artículo seguiremos viendo más cosas sobre el tratamiento de archivos.

Pruebas realizadas en Delphi 7.

1 comentario:

Unknown dijo...

Sin duda es una pagina demaciado buena, por que la explicacion dada para cada aplicacion es tal que no no hay modos de cometer errores en la codificacion, gracias por todos los articulos y explicaciones y espero que sigas adelande, por que es una pagina buenisima

Publicidad