| Apartado anterior | Apartado siguiente |
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.
| Apartado anterior | Apartado siguiente |