TEMA 1.15
FICHEROS SECUENCIALES





1.15.1 - INTRODUCCIÓN AL USO DE FICHEROS

Hasta ahora en nuestros programas para almacenar información hemos utilizado variables que son la forma que tienen los lenguajes de programación de almacenar datos en la memoria RAM del ordenador. Es una forma muy cómoda y extremadamente rápida de manejar datos, pero tiene el grave inconveniente de que toda la información se pierde al finalizar la ejecución del programa.

En el tema de los arrays vimos varios ejemplos de programas donde se pedían los nombres de las personas que viven en un bloque de pisos. Todo funcionaba muy bien pero había que hartarse de escribir todos estos nombres cada vez que entrábamos al programa porque al terminar se pierden. Una solución para evitar esto podría ser incluir los nombres de las personas en el código fuente del programa, pero estos nombres pueden cambiar alguna vez y el usuario del programa no tiene por qué saber como modificar esto. La solución definitiva a este problema consiste en guardar la información en el sistema de archivos de las unidades de disco del ordenador donde permanecerá por tiempo indefinido y podremos recuperarla o modificarla todas las veces que queramos.

La información se almacena en los discos del ordenador en unas estructuras lógicas llamadas ficheros o archivos. Mientras que en una cinta de vídeo si grabamos una película nosotros nos tenemos que preocupar de ver donde está el final para grabar lo siguiente a continuación y no borrar un trozo de lo que ya tenemos, en el ordenador es el sistema operativo (MS-DOS, Linux, Etc.) el que se encarga de controlar en que parte del disco se graban los datos y de ponerle un nombre y situarlo en el sistema de archivos (La FAT y los directorios) para que nosotros podamos acceder a esa información solo con dar el nombre del archivo sin tener que preocuparnos de en que parte del disco está o de si nos vamos a cargar otra información al grabar nuevos datos.

Principalmente hay dos tipos de ficheros: Secuenciales y de acceso aleatorio. Aunque están grabados en los discos de idéntica forma se diferencian de cómo los manejamos nosotros desde nuestro programa. En los siguientes apartados veremos las diferencias y la forma de usar cada uno de ellos.

Ficheros secuenciales son aquellos en que los datos se graban o se leen línea a línea siempre empezando por el principio. Normalmente al abrirlo se copiará su contenido en alguna estructura de variables de nuestro programa para su utilización y posible modificación. Después si hay que guardarlo se hará completamente sustituyendo a toda la información que hubiera anteriormente en el fichero, aunque alguna parte de la información vuelva a ser la misma. El concepto es similar a lo que hacemos con los ficheros de las aplicaciones normales como procesadores de texto o los propios ficheros *.BAS de los programas de QBasic.

Ficheros de acceso directo o acceso aleatorio (No tiene nada que ver con los iconos de acceso directo de Windows) son los que nos permiten leer o modificar un determinado dato sabiendo su posición sin tener que recorrer todos los datos anteriores. El ejemplo más típico de estos ficheros son las tablas de las bases de datos donde podemos añadir, modificar, consultar o borrar la información de una persona (un registro) sin preocuparnos de los demás.

La extensión que elijamos para nuestros ficheros puede ser cualquiera, pero deberemos evitar usar extensiones ya usadas por otros programas que podrían provocar conflictos en Windows. Lo más normal es usar extensiones como .TXT para los ficheros secuenciales que solo contienen texto y .DAT en general para los que pueden contener texto o cualquier otro tipo de caracteres.





1.15.2 - INTRODUCCIÓN A LOSFICHEROS SECUENCIALES

La forma más elemental de trabajar con ficheros es leerlos línea por línea, una tras otra, para irlos analizando o irlos guardando en una estructura de datos de nuestro programa. Si queremos guardarlo se hará de la misma forma, escribiendo líneas una tras otra desde la primera hasta la última. Los ficheros secuenciales normalmente se leen siempre enteros y para guardarlos se puede elegir entre reemplazar completamente su contenido o bien guardar las nuevas líneas a continuación de la s existentes, a lo que llamaremos "Anexar".

Entendemos por "Línea" a una secuencia de caracteres alfanuméricos (Letras, números y signos de puntuación) terminados por el carácter especial de retorno de carro que es el que se genera cuando pulsamos la tecla Enter. Este carácter no lo vemos en la pantalla pero se genera automáticamente al final de lo que escribimos con la orden PRINT, si no ponemos un punto y coma al final, como vimos en el tema de Entrada y Salida.

Para trabajar con ficheros hay que "Abrirlos" especificando su nombre y si queremos leer o escribir en él. A continuación leeremos o escribiremos los datos, según corresponda por el modo en que está abierto, y finalmente lo cerraremos.





1.15.3 - ESCRIBIR INFORMACIÓN
EN FICHEROS SECUENCIALES

Vamos con un ejemplo de un programa que abre un fichero para escritura (lo crea), escribe en él los nombres de los días de la semana y finalmente lo cierra.

OPEN "diasem.txt" FOR OUTPUT AS #1
PRINT #1, "Lunes"
PRINT #1, "Martes"
PRINT #1, "Miércoles"
PRINT #1, "Jueves"
PRINT #1, "Viernes"
PRINT #1, "Sábado"
PRINT #1, "Domingo"
CLOSE #1

Este programa no produce ningún resultado en la pantalla, sólo el rótulo "Presione cualquier tecla y continúe" para indicar que ha terminado.

Si buscamos el fichero diasem.txt y lo abrimos con el bloc de notas de Windows comprobaremos que contiene lo siguiente:

Lunes
Martes
Miercoles
Jueves
Viernes
Sabado
Domingo

Salvo los típicos problemas con los acentos, es lo que esperábamos. Si lo abrimos con el EDIT de MS-DOS veremos los acentos correctamente.

Ahora vamos a comentar detalladamente como funciona el programa.

La primera línea es la más importante cuando trabajamos con ficheros. Si la traducimos al castellano dice algo así como:

Abre "diasem.txt" para salida de datos y llámale #1

Visto esto ya está claro su significado pero de todas formas hay que decir un montón de cosas:

La única cosa que nos puede resultar extraña de la instrucción OPEN es ese #1 que aparece al final. A este numero le llamamos "descriptor de archivo" y nos va a servir para identificar unívocamente al archivo que acabamos de abrir en las ordenes de entrada y salida de nuestro programa hasta que cerremos el archivo. Por lo tanto si abrimos otro archivo antes de cerrar este tendremos que usar el descriptor #2 y así sucesivamente. QBasic nos permite tener abiertos a la vez hasta 255 archivos, pero la memoria disponible y la cláusula FILES del archivo CONFIG.SYS nos limitarán el número. Normalmente no necesitaremos más de uno o dos archivos abiertos a la vez en nuestros programas sencillos.

Siguiendo con el código de nuestro programa a continuación tenemos una serie de instrucciones PRINT que como llevan el descriptor de un archivo escriben en el archivo en lugar de en la pantalla. Podemos usarlas exactamente de la misma forma que cuando las usamos normalmente para escribir en pantalla. No tienen por qué estar todas juntas.

Finalmente lo que hacemos es cerrar el archivo con la orden CLOSE. Conforme vamos escribiendo en el archivo con la orden PRINT la información no se graba en el disco inmediatamente, sino que se va almacenando de forma transparente para el usuario en un área especial de la memoria (El "Buffer" de escritura en disco) o bien en registros de memoria especial dentro de la placa de circutos del propio disco duro y cuando hay información suficiente es cuando el cabezal del disco se mueve y la información se graba físicamente en el disco. Al cerrar el archivo nos aseguramos de que la información que quedara por escribir se grabe realmente en el disco, y que además todos los recursos utilizados, así como el número del descriptor se liberen y queden disponibles para ser usados por otro archivo. Si programamos para un sistema multitarea (Windows 9x lo es, más o menos) es muy importante ocupar los recursos del sistema el menor tiempo posible y por lo tanto deberemos cerrar los archivos que ya no nos hagan falta abiertos aunque todavía vayamos a seguir utilizando otras partes del programa durante más tiempo.

Cuando abrimos un fichero este queda disponible para todos los módulos (Programa principal, procedimientos y funciones) de nuestro programa, con lo que podemos abrirlo desde un módulo, leer o escribir desde otro, y cerrarlo desde otro, pero siempre teniendo en cuenta que solo lo podemos usar después de haberlo abierto y antes de cerrarlo.





1.15.4 - LEER INFORMACIÓN DE FICHEROS SECUENCIALES

En el apartado anterior hemos abierto un archivo para escritura. Ahora vamos a abrir uno existente para leer su contenido. Veamos un ejemplo de un programa que abre el fichero ya existente "diasem.txt" que hemos creado con el programa anterior para mostrarlo en pantalla.

CLS
OPEN "diasem.txt" FOR INPUT AS #1
WHILE NOT EOF(1)
	LINE INPUT #1, linea$
	PRINT linea$
WEND
CLOSE #1

Este programa no produce ningún cambio en el archivo y su resultado por pantalla es:

Lunes
Martes
Miércoles
Jueves
Viernes
Sábado
Domingo

Ahora veamos cómo funciona.

Como se pede ver, la instrucción OPEN es igual a la anterior, sólo varía el modo de apertura que en este caso es INPUT, es decir, entrada de datos, lectura. Si el fichero especificado no existe se producirá un error de tiempo de ejecución. En el apartado de control de errores veremos como detectar si un fichero existe antes de inventar abrirlo.

A continuación vamos leyendo las líneas del archivo. Como no tenemos por qué saber cuantas líneas tiene el archivo, usaremos un bucle MIENTRAS que usa como condición la función EOF (End Of File) que devuelve Verdadero si se ha alcanzado el final del archivo y falso en caso contrario. Observa que a EOF le pasamos como parámetro el descriptor del archivo, pero sin el carácter #. Esto es para poder usar alguna expresión matemática que devuelva el numero del archivo si es que tenemos varios abiertos y no queremos usar el número de forma literal. Dentro del bucle lo que hacemos es leer una línea usando la instrucción LINE INPUT seguida del descriptor del archivo y del nombre de la variable donde la queremos guardar. Esta instrucción permite leer líneas de hasta 255 caracteres que acaben con un carácter especial de retorno de carro (Las que en el ejemplo anterior escribimos con la orden PRINT). Seguidamente escribimos en la pantalla el valor de la variable line$ usando la orden PRINT como hemos hecho siempre.

Finalmente, terminado el bucle, cerramos el archivo para dejar el recurso disponible. Como estaba abierto para lectura no se producirá ningún cambio.

Es muy importante entender que para leer dados de un fichero secuencial debemos usar siempre un bucle MIENTRAS, y no un REPETIR, ya que en este caso intentaríamos leer al menos una línea del fichero antes de comprobar si se ha llegado al final, y se puede dar el caso de que un fichero exista, pero esté completamente vacío con lo que intentaríamos leer más allá del final del final del archivo y generaríamos un error de tiempo de ejecución. En la ayuda de QBasic viene un ejemplo hecho con un bloque DO...LOOP y se puede comprobar que puede que no funcione correctamente en el caso de que el fichero esté vacío.

También podemos decir aquí que si somos nosotros quienes escribimos el archivo a leer usando un editor de texto es conveniente pulsar al final la tecla ENTER una vez para que el cursor pase a la línea de abajo y se genere el carácter de retorno de carro. Si no lo hacemos puede ocurrir que cuando leamos el archivo con nuestro programa de QBasic en algunos casos no se llegue a leer la última línea. Esta indicación también la hace MS-DOS para los ficheros CONFIG.SYS, AUTOEXEC.BAT y los ficheros de proceso pro lotes que nosotros podamos escribir.

Para terminar con la teoría de los ficheros secuenciales vamos a ver un ejemplo de un programa que copia un fichero de texto en otro poniéndole todo el texto en mayúsculas con ayuda de la función UCASE$. El usuario tendrá que escribir los nombres de los dos ficheros, el existente y el nuevo, y el programa los abrirá los dos a la vez, uno para lectura y el otro para escritura.

CLS
INPUT "Nombre del fichero de origen ya existente: ", fich1$
INPUT "Nombre del nuevo fichero: ", fich2$
OPEN fich1$ FOR INPUT AS #1
OPEN fich2$ FOR OUTPUT AS #2
PRINT "Leyendo y grabando, por favor espera..."
WHILE NOT EOF (1)
	LINE INPUT #1, linea$
	PRINT #2, UCASE$(linea$)
WEND
CLOSE
PRINT "Proceso terminado"

El resultado por pantalla podría ser:

Nombre del fichero de origen ya existente: diasem.txt
Nombre del nuevo fichero: diasem2.txt
Leyendo y grabando, por favor espera...
Proceso terminado

Y en los ficheros el que pusiéramos primero no se modificará, mientras que el segundo si no existe se crea y si existe se sobrescribe con el contenido del primero en mayúsculas.

Ahora veamos algunas cosas del programa:

Si escribes el programa en QBasic y lo ejecutas podrás comprobar que puede ser difícil acertar a la primera con el nombre del archivo, y más si ponemos una ruta de directorios, unidades de disco, etc... con lo que se producirá un error de ejecución. También si se te ocurre poner el mismo nombre las dos veces nos dará un error de tiempo de ejecución diciendo que el fichero ya está abierto. En el tema de manejo de errores veremos como se pueden solucionar estos problemas.





1.15.5 - ANEXAR INFORMACIÓN A FICHEROS SECUENCIALES

Cuando abríamos un fichero para escritura usando el modo OUTPUT decíamos que si no existe se crea y si existe se borra su contenido, pero en algunos caso nos puede interesar que el contenido del fichero no se borre y las nuevas líneas de información se añadan al final del fichero ya existente, incrementando su tamaño. Es el caso de los ficheros .LOG que utilizan muchos programas para llevar un registro de acciones o de problemas que puedan surgir y en los que los distintos procesos o programas van añadiendo información conforme va haciendo falta.

La forma de abrirlo es usando el modo APPEND en la instrucción OPEN, por ejemplo:

OPEN "eventos.txt" FOR APPEND as #1

En este caso si no existe el fichero eventos.txt se crea, y si existe se conserva, añadíendose la nueva información al final.

La forma de trabajar con un fichero abierto en modo APPEND es exáctamente la misma que en uno abierto en modo OUTPUT. Hay que recordar que no podemos asegurar que la información se graba físicamente en el fichero hasta que no lo cerremos con la orden CLOSE.

El tamaño de los ficheros usados sólo de esta forma siempre va a crecer y seremos nosotros los encargados de observar que no se hace demasiado grande y de borrarlo cuando ya no nos haga falta la información que contiene.













CuRSo De iNTRoDuCCióN a La PRoGRaMaCióN CoN QBaSiC
© 2004 Juan M. González