TEMA 4: Manejo de Archivos 

        Dentro  de este  apartado vamos  a aprender  que son  y como
utilizar los archivos.  Veremos cual es su finalidad,  que tipos son
los más  comunes y cuales son  sus características principales desde
el punto de vista de la organización de los datos que contiene.

        Para comenzar podemos decir que un ordenador que no tiene la
posibilidad de almacenar sus programas  y datos en un dispositivo de
almacenamiento,  (ya sean  discos, cintas,  etc.) no  es mas que una
calculadora.   En   la   actualidad,   cualquier   ordenador   posee
dispositivos de  almacenamiento, ya sean  internos (discos duros)  o
externos  (disquetes,  cartuchos,  cintas,  etc.).  La  finalidad es
obvia, la de poder guardar  los datos para su posterior recuperación
y tratamientos de los mismos en otras sesiones.

        Un común error entre los principiantes es el de confundir la
memoria de tipo RAM con la capacidad de almacenamiento del disco que
acompaña  al  ordenador.  La  RAM  (Memoria  de Acceso Aleatorio) es
denominada memoria volátil, ya que una vez que se apaga el ordenador
la  información  que  esta  contenía  se  pierde.  La  RAM  se  mide
generalmente en MegaBytes,  aunque con el paso del  tiempo la unidad
puede  cambiar (1  Mb son  1024 Ks,  a su  vez 1  K es 1024 bytes, y
finalmente 1 Byte son 8 bits,  siendo esta última la cantidad mínima
de información  que puede procesar  un computador). La  capacidad de
los  dispositivos  de  almacenamiento  (entiéndase disquetes, discos
duros, cintas, CD-ROM, etc.) se mide  en las mismas unidades, es por
ello que la gente suele confundir la memoria RAM de un ordenador con
la capacidad de almacenamiento que suele tener en un disco duro.

        La memoria RAM es uno de los aspectos que limita la potencia
de un ordenador, cuanta más  RAM tengamos, mayores programas y datos
podremos  almacenar en  ella y  menos accesos  a los dispositivos de
almacenamiento tendremos que realizar.

        Los archivos o ficheros se  almacenan en los dispositivos de
almacenamiento,   para   como   dijimos   anteriormente  puedan  ser
recuperados sus datos en sesiones futuras.

        De  esta forma,  podemos definir  un fichero  como un objeto
concebido  para  el  almacenamiento  permanente  de  la información.
Información que puede ser  organizada de diferente forma dependiendo
del trato que le vayamos a dar.

4.1 Nociones de dispositivos de almacenamiento.

        En el  apartado anterior hemos aprendido  que la información
puede ser  volcada en dispositivos  de almacenamiento permanente,  a
los cuales nos hemos referido  como disquetes, discos duros, cintas,
CD-ROM, etc.

        Estos medios  de almacenamiento se pueden  diferenciar en el
modo en el que la información puede ser accesible.

        Así  definimos  dispositivos  de  almacenamiento  o soportes
secuenciales   a  aquellos   donde  la   información  es   accesible
secuencialmente, es decir, para leer o escribir un dato determinado,
antes tenemos que pasar por todos los datos que le preceden. De esta
forma, en una cinta magnética los  datos se graban y leen uno detrás
de otro.  Y no podemos ir  directamente a uno en  concreto sin pasar
antes por los demás.

        De igual forma se  define soportes direccionables a aquellos
donde   la   superficie   de   almacenamiento   es  independiente  e
individualmente direccionable,  es decir, podemos acceder  a un dato
en concreto sin tener que pasar antes por los demás. Este es el caso
de los discos. Por poner un  ejemplo, en un disco de vinilo, podemos
elegir la canción  que queremos tan sólo con poner  el cabezal en el
comienzo de la misma. Sin embargo,  en una cinta de casete, para oír
una  canción  determinada  antes  debemos  pasar  todas  las  que le
preceden.

        En los  dispositivos de almacenamientos  hay que diferenciar
las  direcciones en  las que  se almacenan  los datos.  De este modo
tenemos dos tipos de direcciones:  direcciones absolutas o físicas y
direcciones relativas o lógicas.

        Las direcciones absolutas o  físicas son las direcciones con
las que  juega el ordenador  de forma física  en el dispositivo,  es
decir, cuando el ordenador se refiere  a un dato en concreto lo esta
haciendo  por el  conocimiento de  ciertos datos  que identifican la
posición física  del dato en  el soporte de  almacenamiento. Así, el
ordenador  juega con  parámetros tales  como: unidad,  cabeza, cara,
pista o cilindro y sector.

        Por el  contrario, las direcciones  relativas o lógicas  son
con las que  vamos a jugar nosotros, una vez  que abrimos el fichero
para  realizar operaciones  de Input/output  (I/O) nos  referimos al
dato con un índice numérico, es decir, el registro número x.

4.2 Ficheros de Acceso Secuencial.

        Un Fichero de Acceso Secuencial es aquel donde los registros
están   ubicados   consecutivamente    sobre   un   dispositivo   de
almacenamiento.  De  tal  forma  que  para  acceder  a  un  registro
determinado  'd'  debemos  pasar   obligatoriamente  por  todos  los
registros que le preceden.

        Suponiendo  una  cinta  magnética,  para  leer  registro 'd'
debemos pasar antes por el 'a', 'b', 'c'.

       ----------------------   Para leer el registro 'd', la cabeza
     +- a   b   c   d   ...     lectora, deberá pasar antes por los
     | ----------------------   que le preceden.
     +> cabeza lectora.


        Un Fichero  de Acceso Secuencial puede  ser almacenado tanto
en un dispositivo de almacenamiento direccional como secuencial, sin
embargo los  Ficheros de Acceso Directo  e Indexado únicamente puede
ubicarse en dispositivos de almacenamiento direccional.

4.2.1 Ejemplo de I/O en Ficheros Secuenciales.

        Pasemos  a   ver  un  ejemplo  de   como  utilizar  ficheros
secuenciales,   pero  antes   veamos  las   sentencias  básicas  que
utilizaremos.

- Abrir secuencial (variable)

        Esta sentencia  abre un fichero  para el acceso  secuencial.
Donde la variable contiene el nombre del fichero a abrir.

- Cerrar (variable)

        Esta sentencia cierra un  fichero donde la variable contiene
el nombre del fichero a cerrar.

- Leer/Escribir/Reescribir (variable1,variable2)

        Esta  sentencia permite  la  lectura  o re/escritura  de los
datos  que  contiene  la  variable2  en  un  fichero  de  nombre, el
determinado en la variable1.

- Iniciar operaciones de lectura y/o escritura en (variable)

        Esta sentencia debe incluirse después  de abrir un fichero y
determina prácticamente el  tipo de acceso que vamos  a realizar, es
decir, si abrimos el fichero para leer y/o escribir.

- NO fin (variable)

        Esta  función devuelve  el valor  lógico 'true'  si no se ha
encontrado el final del fichero;  y devuelve el valor lógico 'false'
si se ha encontrado el final del fichero.

- Error (variable)

        Esta  función  devuelve  el  código  del  error producido al
realizar  cualquier  operación  anterior.  Por  defecto,  la función
devuelve un cero cuando no se ha producido ningún error.


Ejemplo: En este  ejemplo se intenta dar aun  visión general del uso
de ficheros secuenciales.

+-Algoritmo Copiar_fichero
|
|   Fichero de enteros Origen,Destino
|   Variable entera x
|
|   Escribir ("Indique el nombre del fichero Origen:");
|   Leer Origen
|   Escribir ("Indique el nombre del fichero Destino:")
|   Leer Destino
|
|   Abrir secuencial (Origen)
|   Iniciar lectura en (Origen)
|   Abrir secuencial (Destino)
|   Iniciar escritura en (Destino)
|
| +-Mientras (NO fin(Origen)) hacer
| |   Leer (Origen,x)
| |   Escribir (Destino,x)
| +-Finmientras
|
|   Escribir ("Fichero Copiado:")
|
|   Cerrar (Origen)
|   Cerrar (Destino)
|
+-Final

        Nota: Obsérvese que la variable utilizada en las operaciones
de  lectura  y/o  escritura,  deben   ser  del  mismo  tipo  que  la
declaración del fichero.

4.3 Registros Estructurados.

        Hasta ahora hemos  visto como los registros eran  de un tipo
único,  es decir,  eran todos  carácter, enteros,  reales, etc.  Sin
embargo, hay situaciones en las que debemos realizar agrupaciones de
tipos  para  formar  un  registro  estructurado.  Así,  un  registro
estructurado esta formado por un conjunto de variables de diferentes
tipos que  denominaremos campos. De  este modo podemos  decir que un
registro estructurado es un conjunto de campos.

Ejemplo:

+-Registro datos-alumno
|   Cadena nombre
|   Cadena apellido1
|   Cadena apellido2
|   Cadena NIF
|   Cadena curso
|   Cadena telefono
|   Cadena fecha_nac
|   ...
+-Finregistro

        De  esta  forma,  podemos  leer  y  escribir  en  un fichero
registros  estructurados que  nos permitirán  un almacenamiento  más
lógico y ordenado de los datos.

        Al generar un registro es como si estuviésemos definiendo un
nuevo tipo  de variable independiente de  las ya existente (enteras,
reales, cadena, etc.).

        Este tipo de  registros o record se utiliza  para agrupar la
información que se desee volcar a un fichero determinado.

4.4 Ficheros de Acceso Directo.

        Un  Fichero  de  Acceso  Directo  (es  también denominado de
acceso  aleatorio)  es  aquel  que  se  encuentra  almacenado  en un
dispositivo direccionable; y donde sus registros poseen un campo que
denominaremos campo  clave y que  identifica inequívocamente a  cada
registro.  En los ficheros de acceso directo el campo  clave  es  el
número   del   registro   en  el  fichero.   Así  se  establece  una
correspondencia directa entre  los  valores  del  campo  clave y las
direcciones lógicas en el soporte.  Los registros se almacenan según
el orden de entrada y no quedan ordenados.

        De esta forma en un  Fichero de Acceso Directo nos referimos
a un  registro  por  medio  de  su  posición  en este. Así, podremos
obtener el reg número 4 sin pasar antes por los demás.

          +-----+------------+ 
          | Reg |  D A T O   | 
          +-----+------------|        Fíjese   que los datos  están
          |  1  |  LUIS      |     almacenados  en el  orden  en el
          |  2  |  CARLOS    |     que han sido introducidos por el
          |  3  |  TERESA    |     usuario.
          |  4  |  JOAQUIN   | 
          |  5  |  INMA      |       Accedemos a los datos por medio
          |  6  |  JOSE      |    del   valor  de  la  posición  del
          +-----+------------+    registro.

        La tabla anterior se denomina tabla de acceso.   Esta  tabla
relaciona  de  forma  única  el  número del registro con el registro
correspondiente, así el reg número 2 corresponde al dato Carlos.

4.4.1 Ejemplo de I/O en Ficheros de Acceso Directo.

        Las sentencias  que manejan los  ficheros de acceso  directo
son las mismas, sólo que poseen el prefijo directo.

Ejemplo:

+-Algoritmo Contador_de_registros.
|
|   Fichero de enteros F
|   Variable entera x,contador
|
|   Abrir directo (F)
|   Iniciar lectura (F)
|   contador = 0
| +-Mientras (NO fin(F)) hacer
| |   Leer directo(F,x)
| |   contador=contador+1
| +-Finmientras
|   Cerrar(F)
|
|   Escribir  ("El fichero:";F;"posee:";contador;"reg.")
|
+-Final

4.5 Ficheros de Acceso Indexado.

        Un  Fichero de  Acceso Indexado  es aquel  que se  encuentra
almacenado en  un dispositivo direccionable;  y donde sus  registros
poseen  un  campo  que  denominaremos  campo  clave  principal y que
identifica inequívocamente a cada  registro. La clave principal debe
ser aquel campo del registro estructurado que tenga siempre un valor
diferente  a  los  ya  introducidos,  es  decir,  dentro del fichero
Indexado no puede haber dos registros con los campos clave principal
iguales.

        Además del campo clave principal pueden existir otros campos
claves secundarios que  realizan la misma tarea que  el campo clave,
sin embargo, sus valores pueden repetirse. En los Ficheros de Acceso
Indexado el  campo clave puede  ser cualquiera de  los campos de  un
registro estructurado. Así se  establece una correspondencia directa
entre  los valores  del campo  clave  y  el propio  registro al  que
pertenece. Los registros se  almacenan ordenados alfabéticamente por
el campo clave,  esto nos facilita la búsqueda  y listados ordenados
por los distintas claves.

        Para cada  campo clave, el  fichero genera una  tabla, donde
dicha clave  aparece ordenada alfabéticamente y  se relaciona con la
dirección de los datos.

        De esta forma en un Fichero de Acceso Indexado nos referimos
a un  registro  por  medio  de  alguna  de  las  claves que posea el
fichero, tanto  la principal como  la secundaria. Es  decir, leer el
registro cuya  clave principal sea: 46.399.554,  en este caso leería
el registro  correspondiente a INMA. También  podríamos haber dicho,
leer los registro cuya clave secundaria sea la Edad=16 y primero nos
leería  el registro  correspondiente a  los datos  de Luis  y en  la
siguiente  petición de  lectura los  datos de  Teresa. La diferencia
entre clave principal  y secundaria, está en que  la clave principal
es  única  (relacionando  así  inequívocamente  al  registro  al que
pertenece) mientras que las claves principales puede ser iguales.

               +------------+-------------------------------------+ 
               |   Clave    |   Clave        Clave       Clave    | 
               | Principal  | Secundaria   Secundaria  Secundaria | 
               +--------------------------------------------------+ 
     +---------+------------+------------+-------------+--------+ 
     | (Direc) |  (D.N.I.)  |  (Nombre)  | (Provincia) | (Edad) | 
     +---------|------------|------------|-------------|--------+ 
     |    1    | 55.366.546 |  LUIS      | Las Palmas  |   16   | 
     |    2    | 42.386.225 |  CARLOS    | Salamanca   |   17   | 
     |    3    | 32.387.203 |  TERESA    | Oviedo      |   16   | 
     |    4    | 46.399.554 |  INMA      | Palencia    |   20   | 
     |    5    | 60.643.434 |  JOAQUIN   | Salamanca   |   17   | 
     |    6    | 22.543.986 |  JOSE      | Las Palmas  |   23   | 
     +---------+------------+------------+-------------+--------+ 

        Como podemos  observar, esto sería un  ejemplo de un fichero
indexado.

        Para cada  campo clave, el  fichero genera una  tabla, donde
dicha clave  aparece ordenada alfabéticamente y  se relaciona con la
dirección de los datos. Así las tablas para la clave principal (DNI)
y la clave secundaria (Nombre) serían:

      +------------+---------+      +------------+---------+ 
      | (D.N.I.)   | (Direc) |      |  (Nombre)  | (Direc) | 
      +------------+---------|      +------------+---------+ 
      | 22.543.986 |    6    |      |  CARLOS    |    2    | 
      | 32.387.203 |    3    |      |  INMA      |    4    | 
      | 42.386.225 |    2    |      |  JOAQUIN   |    5    | 
      | 46.399.554 |    4    |      |  JOSE      |    6    | 
      | 55.366.546 |    1    |      |  LUIS      |    1    | 
      | 60.643.434 |    6    |      |  TERESA    |    3    | 
      +------------+---------+      +------------+---------+ 
    +--------------------------+  +---------------------------+ 
    | Tabla de Ac. Clave Princ.|  | Tabla de Ac. Clave Secund.| 
    +--------------------------+  +---------------------------+ 

        Obsérvese    como    ambas    tablas    aparecen   ordenadas
alfabéticamente (o  de menor a  mayor en el  caso del DNI).  Como ya
dijimos,  esto nos  da grandes   facilidades a  la hora  de realizar
búsquedas y/o listados ordenados.

4.5.1 Ejemplo de I/O en Ficheros de Acceso Indexado.

        Pasemos  a   ver  un  ejemplo  de   como  utilizar  ficheros
indexados,   pero   antes   veamos   las   sentencias   básicas  que
utilizaremos.

- Abrir indexado (variable,KEY=...)

        Esta  sentencia abre  un  fichero  para el  acceso indexado.
Donde la variable contiene el nombre del fichero a abrir y en 'KEY='
ponemos  los campos  claves separados  por comas,  comenzando por el
campo clave principal.

- Cerrar (variable)

        Esta sentencia cierra un  fichero donde la variable contiene
el nombre del fichero a cerrar.

- Leer/Escribir/Reescribir (variable1,KEY=,variable2)

        Esta  sentencia permite  la  lectura  o re/escritura  de los
datos  por medio  de un   campo clave  (principal o  secundaria) que
debemos  poner después  de 'KEY='.  El contenido  será almacenado  o
mandado por la variable2 en un  fichero de nombre, el determinado en
la variable1.

- Iniciar operaciones de lectura y/o escritura en (variable)

        Esta sentencia debe incluirse después  de abrir un fichero y
determina prácticamente el  tipo de acceso que vamos  a realizar, es
decir, si abrimos el fichero para leer y/o escribir.

- NO fin (variable)

        Esta  función devuelve  el valor  lógico 'true'  si no se ha
encontrado el final del fichero;  y devuelve el valor lógico 'false'
si se ha encontrado el final del fichero.

- Error (variable)

        Esta  función  devuelve  el  código  del  error producido al
realizar  cualquier  operación  anterior.  Por  defecto,  la función
devuelve un cero cuando no se ha producido ningún error.

        Las sentencias  que manejan los ficheros  de acceso Indexado
son  las mismas  que hemos  utilizado en  los ficheros secuenciales,
sólo que poseen el prefijo Indexado  y en las operaciones de lectura
y/o escrituras hay que indicar la clave ('KEY=').

Ejemplo:

+-Algoritmo Buscar_Persona.
|
| +-Registro estructurado datos_personales
| |   Variable cadena dni
| |   Variable cadena nombre
| |   Variable cadena tlf
| |   Variable cadena provincia
| +-Finregistro
|
|   Fichero de datos_personales F
|   Variable cadena documento
|   Variable de datos_personales dato
|
|   Escribir "Indique el DNI de la persona a buscar"
|   Leer documento
|
|   Abrir indexado (F,KEY=dni,nombre)
|   Iniciar lectura (F)
|
|   Leer (F,KEY=documento,dato)
| +-Si error(F)<>0 entonces
| |   Escribir "Ese registro no existe."
| +-Sino
| |   Escribir "      DNI: ";dato.dni
| |   Escribir "   Nombre: ";dato.nombre
| |   Escribir "      Tlf: ";dato.tlf
| |   Escribir "Provincia: ";dato.provincia
| +-Finsi
|
|   Cerrar (F)
+-Final

4.6 Función de Hashing.

        Muchas veces surge el caso del  que el fichero es tan grande
que la  tabla no puede mantener  una ordenación eficaz debido  a que
cuando  introducimos un  nuevo dato  debe hacerse  un espacio  en la
misma para albergar a éste. Es por ello que recurrimos al "Hashing".

        El  "Hashing" consiste  simplemente en  relacionar la  clave
principal con la dirección por medio de una fórmula matemática. Así,
antes de introducir  datos se crean unos espacios  para albergar las
futuras modificaciones y adiciones  de  datos.  Este método crea una
serie de conjuntos llamados "Buckets".  A cada Bucket le corresponde
un número que será el que  devuelva la fórmula matemática.  A su vez
los Buckets poseen un número que determina  la  cantidad  máxima  de
claves que pueden almacenarse en él.

        De  esta manera  cuando vamos  a buscar  el dato "Manolo" el
Hashing  nos determina  la posición  del conjunto  (Buckets). En ese
conjunto habrá otra  serie de datos a los  cuales les corresponde el
mismo  valor de  la función  Hashing. La  búsqueda ahora  se hará de
forma secuencial a lo largo del Bucket.

Veamos un ejemplo:

        Bucket  Clave Prin.   Claves Secundarias
       +-----+------------+----------------------+
       |     |   Manolo   |       . . .          |
       |     +------------+----------------------+
       |  35 |   Manuel   |       . . .          |    Al número  104
       +-----+------------+----------------------+    se le denomina 
    +--| 104 |   Manuela  |       . . .          |    puntero     de 
    |  +-----+------------+----------------------+    desbordamiento
    |  |     |   Natalia  |       . . .          |
    |  |     +------------+----------------------+
    |  |  36 |   Naranjo  |       . . .          |
    |  |     +------------+----------------------+
    |  |     |   Noelia   |       . . .          |
    |  +-----+------------+----------------------+
    |         .
    |         .
    |         .
    |  +-----+------------+----------------------+
    |  |     |   Mark     |       . . .          |
    |  |     +------------+----------------------+
    +->| 104 |   Miguel   |       . . .          |
       |     +------------+----------------------+
       |     |   María    |       . . .          |
       +-----+------------+----------------------+

4.6.1 Gestión de las colisiones.

        En este  método parecen una  serie de conflictos  cuando las
claves  son muy  parecidas, como  podemos observar  para claves casi
idénticas, el Hashing nos devuelve el mismo Bucket. Esto implica que
el Bucket  puede llenarse de  datos; cuando esto  ocurre la solución
está en un puntero que existe  en cada Bucket que determina el salto
a  otro Bucket.  Así, cuando se llena el Bucket número 35, existe un
salto de éste al número 104 (otro Bucket) que posee datos del  mismo
tipo, que también puede rebosarse y apuntar a otro Bucket secundario
y así sucesivamente.

        Ahora  es cuando  surgen los  problemas. Cuando  un dato  se
borra de un Bucket hay que  reorganizar la información para no dejar
espacios en blanco dentro de la  tabla. Esto se realiza por medio de
un  empaquetamiento  Packed.  Sin  embargo  cuando  se va a realizar
muchas modificaciones  y/o borrados y  el fichero es  muy grande, es
aconsejable hacer una actualización de los datos del fichero.


4.7 Esquema Básico de los tres tipos de Organización.

* Fichero de Acceso Secuencial:

        - Almacenamiento en dispositivo secuencial o direccionable.
        - No existe campos claves que relacione a algún registro.
        - Los datos están almacenados en el orden en el que han sido
        introducidos.
        - El acceso a los registros es únicamente secuencial.

* Fichero de Acceso Directo:

        - Almacenamiento en dispositivo direccionable.
        - Existe en  los registros un  campo denominado campo  clave
        que hace referencia inequívoca a dicho registro a través del
        número  de  registro.   
        - Los datos están almacenados en el orden en el que han sido
        introducidos.
        - El acceso a los registros  puede  ser  tanto  aleatorio  a
        través del campo clave como secuencial.


* Fichero de Acceso Indexado:

        - Almacenamiento en dispositivo direccionable.
        - Existe en  los registros un  campo denominado campo  clave
        principal  y campo  clave secundario,  que hacen  referencia
        inequívoca a dicho registro.
        - Los datos están almacenados en  el orden alfabético por el
        campo clave.
        - El  acceso a  los registros  puede ser  tanto aleatorio  a
        través del campo clave como secuencial.
        - El acceso Indexado-Secuencial permite el acceso como si se
        tratara de un fichero secuencial,  sin embargo, los datos no
        saldrán en  el orden en  el que fueron  introducidos sino en
        orden alfabético por el campo que estamos leyendo.