Tema 11: Manejo de ficheros (3: Ficheros con tipo)
Curso: Curso de Pascal, por Nacho Cabanes
Curso de Pascal. Tema 11: Manejo de ficheros.
Tema 11.3. Manejo de ficheros (3) - Ficheros con tipo.
Hemos visto cómo acceder a los ficheros de texto, tanto para leerlos como para escribir en ellos. Ahora nos centraremos en lo que vamos a llamar "ficheros con tipo".
Estos son ficheros en los que cada uno de los elementos que lo integran es del mismo tipo (como vimos que ocurre en un array).
En los de, texto se podría considerar que estaban formados por elementos iguales, de tipo "char", pero ahora vamos a llegar más allá, porque un fichero formado por datos de tipo "record" sería lo ideal para empezar a crear nuestra propia agenda.
Una vez que se conocen los ficheros de texto, no hay muchas diferencias a la hora de un primer manejo: debemos declarar un fichero, asignarlo, abrirlo, trabajar con él y cerrarlo.
Pero ahora podemos hacer más cosas también. Con los de texto, el uso habitual era leer línea por línea, no carácter por carácter. Como las líneas pueden tener cualquier longitud, no podíamos empezar por leer la línea 4 (por ejemplo), sin haber leído antes las tres anteriores. Esto es lo que se llama ACCESO SECUENCIAL.
Ahora sí que sabemos lo que va a ocupar cada dato, ya que todos son del mismo tipo, y podremos aprovecharlo para acceder a una determinada posición del fichero cuando nos interese, sin necesidad de pasar por todas las posiciones anteriores. Esto es el ACCESO ALEATORIO (o directo).
La idea es sencilla: si cada ficha ocupa 25 bytes, y queremos leer la número 8, bastaría con "saltarnos" 25*7=175 bytes.
Pero Turbo Pascal (y muchos de los compiladores que nacieron después de él,como Free Pascal) nos lo facilita más aún, con una orden, seek, que permite saltar a una determinada posición de un fichero sin tener que calcular nada nosotros mismos. Veamos un par de ejemplos...
Primero vamos a introducir varias fichas en un fichero con tipo:
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Crea un fichero "con }
{ tipo" }
{ CREAFT.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Free Pascal 2.2.0w }
{ - Turbo Pascal 7.0 }
{--------------------------}
program IntroduceDatos;
type
ficha = record (* Nuestras fichas *)
nombre: string [80];
edad: byte
end;
var
fichero: file of ficha; (* Nuestro fichero *)
bucle: byte; (* Para bucles, claro *)
datoActual: ficha; (* La ficha actual *)
begin
assign( fichero, 'basura.dat' ); (* Asignamos *)
rewrite( fichero ); (* Abrimos (escritura) *)
writeln(' Te iré pidiendo los datos de cuatro personas...' );
for bucle := 1 to 4 do (* Repetimos 4 veces *)
begin
writeln(' Introduce el nombre de la persona número ', bucle);
readln( datoActual.nombre );
writeln(' Introduce la edad de la persona número ', bucle);
readln( datoActual.edad );
write( fichero, datoActual ); (* Guardamos el dato *)
end;
close( fichero ); (* Cerramos el fichero *)
end.
Debería resultar fácil. La única diferencia con lo que ya habíamos visto es que los datos son de tipo "record" y que el fichero se declara de forma distinta, con "file of TipoBase".
Ahora vamos a ver cómo leeríamos sólo la tercera ficha de este fichero de datos que acabamos de crear:
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Lee de un fichero }
{ "con tipo" }
{ LEEFT.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Free Pascal 2.2.0w }
{ - Turbo Pascal 7.0 }
{ - Turbo Pascal 5.0 }
{ - Surpas 1.00 }
{--------------------------}
program LeeUnDato;
type
ficha = record
nombre: string [80];
edad: byte
end;
var
fichero: file of ficha;
bucle: byte;
datoActual: ficha;
begin
assign( fichero, 'basura.dat' );
reset( fichero ); (* Abrimos (lectura) *)
seek( fichero, 2 ); (* <== Vamos a la ficha 3 *)
read( fichero, datoActual ); (* Leemos *)
writeln(' El nombre es: ', datoActual.nombre );
writeln(' La edad es: ',datoActual.edad );
close( fichero ); (* Y cerramos el fichero *)
end.
Espero que el listado sea autoexplicativo. La única cosa que merece la pena comentar es eso del "seek(fichero, 2)": La posición de las fichas dentro de un fichero de empieza a numerar en 0, que corresponderá a la primera posición. Así, accederemos a la segunda posición con un 1, a la tercera con un 2, y en general a la "n" con "seek(fichero,n-1)".
Ejemplo: mini-agenda, con ficheros.
A partir de la mini-agenda que comenzamos en el tema 7 y que ampliamos en el tema 8, podríamos mejorarla para que los datos se guarden en un fichero con tipo. Los cambios podrían ser:
- Se cargan los datos al principio de la sesión, pero sólo si existe el fichero de datos; si el fichero no existe, el programa no debe fallar, sino, en todo caso, avisar.
- Tras cada modificación (por ahora, sólo tras añadir una nueva ficha), se vuelcan todos los datos a fichero, de forma que en caso de pérdida de corriente eléctrica o cualquier otro problema, todo esté guardado.
Podría ser algo como:
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Ejemplo de "Agenda": }
{ Permite añadir datos, }
{ mostrar, buscar. }
{ Usa funciones. }
{ AGENDA3.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Free Pascal 2.4.0 }
{--------------------------}
program Agenda3;
type
tipoPersona = record
nombre: string;
email: string;
anyoNacimiento: integer;
end;
const
capacidad = 1000;
var
gente: array[1..capacidad] of tipoPersona; { Los datos }
cantidad: integer; { Cantidad de datos existentes }
terminado: boolean;
procedure MostrarMenu;
begin
WriteLn('Agenda');
WriteLn;
WriteLn('1- Añadir una nueva persona');
WriteLn('2- Ver nombres de todos');
WriteLn('3- Buscar una persona');
WriteLn('0- Salir');
end;
function LeerOpcion: integer;
var
opcion: integer;
begin
Write('Escoja una opción: ');
ReadLn(opcion);
WriteLn;
if (opcion = 0) then terminado := true;
LeerOpcion := opcion;
end;
procedure CargarDatos;
var
fichero: file of tipoPersona;
i: integer;
begin
assign(fichero, 'agenda.dat');
{$I-}
reset(fichero);
{$I+}
if ioResult <> 0 then
WriteLn('No había fichero de datos. Se creará.')
else
begin
cantidad := filesize(fichero);
for i := 1 to cantidad do
Read(fichero, gente[i]);
close(fichero);
end;
end;
procedure GrabarDatos;
var
fichero: file of tipoPersona;
i: integer;
begin
assign(fichero, 'agenda.dat');
{$I-}
rewrite(fichero);
{$I+}
if ioResult <> 0 then
WriteLn('No se ha podido grabar!')
else
begin
for i := 1 to cantidad do
Write(fichero, gente[i]);
close(fichero);
end;
end;
procedure NuevoDato;
begin
if (cantidad < capacidad) then
begin
inc(cantidad);
WriteLn('Introduciendo la persona ', cantidad);
Write('Introduzca el nombre: ');
ReadLn(gente[cantidad].nombre);
Write('Introduzca el correo electrónico: ');
ReadLn(gente[cantidad].email);
Write('Introduzca el año de nacimiento: ');
ReadLn(gente[cantidad].anyoNacimiento);
WriteLn;
GrabarDatos;
end
else
WriteLn('Base de datos llena');
end;
procedure MostrarDatos;
var
i: integer;
begin
if cantidad = 0 then
WriteLn('No hay datos')
else
for i := 1 to cantidad do
WriteLn(i, ' ', gente[i].nombre);
WriteLn;
end;
procedure BuscarDatos;
var
textoBuscar: string;
encontrado: boolean;
i: integer;
begin
Write('¿Qué texto busca? ');
ReadLn( textoBuscar );
encontrado := false;
for i := 1 to cantidad do
if pos (textoBuscar, gente[i].nombre) > 0 then
begin
encontrado := true;
WriteLn( i,' - Nombre: ', gente[i].nombre,
', Email: ', gente[i].email,
', Nacido en: ', gente[i].anyoNacimiento);
end;
if not encontrado then
WriteLn('No se ha encontrado.');
WriteLn;
end;
procedure AvisarFin;
begin
WriteLn;
WriteLn('Saliendo...');
WriteLn;
end;
procedure AvisarError;
begin
WriteLn;
WriteLn('Opción incorrecta!');
WriteLn;
end;
{Cuerpo del programa principal}
begin
terminado := false;
cantidad := 0;
CargarDatos;
repeat
MostrarMenu;
case LeerOpcion of
1: NuevoDato;
2: MostrarDatos;
3: BuscarDatos;
0: AvisarFin;
else AvisarError;
end; { Fin de "case" }
until terminado;
end.
Actualizado el: 04-08-2006 17:23