Hasta el capítulo anterior no habíamos visto ninguna forma de guardar permanentemente los datos y resultados de nuestros programas. En este capítulo vamos a verlo mediante el manejo de ficheros.
En el capítulo anterior usábamos la redirección para crear ficheros. Este es un sistema poco flexible para manejar ficheros. Ahora vamos a crear y modificar ficheros usando las funciones estándar del C.
Es importante indicar que los ficheros no son únicamente los archivos que guardamos en el disco duro, en C todos los dispositivos del ordenador se tratan como ficheros: la impresora, el teclado, la pantalla,...
Para entrar en materia vamos a analizar un ejemplo que lee un fichero de texto y lo muestra en la pantalla:
#include <stdio.h> int main() { FILE *fichero; char letra; fichero = fopen("origen.txt","r"); if (fichero==NULL) { printf( "No se puede abrir el fichero.\n" ); exit( 1 ); } printf( "Contenido del fichero:\n" ); letra=getc(fichero); while (feof(fichero)==0) { printf( "%c",letra ); letra=getc(fichero); } if (fclose(fichero)!=0) printf( "Problemas al cerrar el fichero\n" ); }
Vamos a analizar el ejemplo poco a poco:
Todas las funciones de estrada/salida estándar usan este puntero para conseguir información sobre el fichero abierto. Este puntero no apunta al archivo sino a una estructura que contiene información sobre él.
Esta estructura incluye entre otras cosas información sobre el nombre del archivo, la dirección de la zona de memoria donde se almacena el fichero, tamaño del buffer.
Como dato curioso se adjunta la definición de la estructura FILE definida en el fichero stdio.h:
typedef struct { int _cnt; char *_ptr; char *_base; int _bufsiz; int _flag; int _file; char *_name_to_remove; int _fillsize; } FILE;
Ahora nos toca abrir el fichero. Para ello usamos la función fopen. Esta función tiene el siguiente formato:
FILE *fopen(const char *nombre_fichero, const char *modo);
En el ejemplo usábamos:
fichero = fopen("origen.txt","r");
El nombre de fichero se puede indicar directamente (como en el ejemplo) o usando una variable.
El fichero se puede abrir de diversas formas. Esto se especifica con el parámetro modo. Los modos posibles son:
r | Abre un fichero existente para lectura. |
w | Crea un fichero nuevo (o borra su contenido si existe) y lo abre para escritura. |
a | Abre un fichero (si no existe lo crea) para escritura. El puntero se sitúa al final del archivo, de forma que se puedan añadir datos si borrar los existentes. |
Se pueden añadir una serie de modificadores siguiendo a los modos anteriores:
b | Abre el fichero en modo binario. |
t | Abre el fichero en modo texto. |
+ | Abre el fichero para lectura y escritura. |
Ejemplos de combinaciones:
Una cosa muy importante después de abrir un fichero es comprobar si realmente está abierto. El sistema no es infalible y pueden producirse fallos: el fichero puede no existir, estar dañado o no tener permisos de lectura.
Si intentamos realizar operaciones sobre un puntero tipo FILE cuando no se ha conseguido abrir el fichero puede haber problemas. Por eso es importante comprobar si se ha abierto con éxito.
Si el fichero no se ha abierto el puntero fichero (puntero a FILE) tendrá el valor NULL, si se ha abierto con éxito tendrá un valor distinto de NULL. Por lo tanto para comprobar si ha habido errores nos fijamos en el valor del puntero:
if (fichero==NULL) { printf( "No se puede abrir el fichero.\n" ); exit( 1 ); }
Si fichero==NULL significa que no se ha podido abrir por algún error. Lo más conveniente es salir del programa. Para salir utilizamos la función exit( 1 ), el 1 indica al sistema operativo que se han producido errores.
Ahora ya podemos empezar a leer el fichero. Para ello podemos utilizar la función getc, que lee los caracteres uno a uno. Se puede usar también la función fgetc (son equivalentes, la diferencia es que getc está implementada como macro). Además de estas dos existen otras funciones como fgets, fread que leen más de un carácter y que veremos más adelante.
El formato de la función getc (y de fgetc) es:
int getc(FILE *fichero);
En este caso lo usamos como:
letra = getc( fichero );
Tomamos un carácter de fichero, lo almacenamos en letra y el puntero se coloca en el siguiente carácter.
Cuando entramos en el bucle while, la lectura se realiza hasta que se encuentre el final del fichero. Para detectar el final del fichero se pueden usar dos formas:
En el ejemplo hemos usado la función feof. Esta función es de la forma:
int feof(FILE *fichero);
Esta función comprueba si se ha llegado al final de fichero en cuyo caso devuelve un valor distinto de 0. Si no se ha llegado al final de fichero devuelve un cero. Por eso lo usamos del siguiente modo:
while ( feof(fichero)==0 ) o while ( !feof(fichero) )
La segunda forma que comentaba arriba consiste en comprobar si el carácter leído es el de fin de fichero EOF:
while ( letra!=EOF )
Cuando trabajamos con ficheros de texto no hay ningún problema, pero si estamos manejando un fichero binario podemos encontrarnos EOF antes del fin de fichero. Por eso es mejor usar feof.
Una vez realizadas todas las operaciones deseadas sobre el fichero hay que cerrarlo. Es importante no olvidar este paso pues el fichero podría corromperse. Al cerrarlo se vacían los buffers y se guarda el fichero en disco. Un fichero se cierra mediante la función fclose(fichero). Si todo va bien fclose devuelve un cero, si hay problemas devuelve otro valor. Estos problemas se pueden producir si el disco está lleno, por ejemplo.
if (fclose(fichero)!=0) printf( "Problemas al cerrar el fichero\n" );
La función fgets es muy útil para leer líneas completas desde un fichero. El formato de esta función es:
char *fgets(char *buffer, int longitud_max, FILE *fichero);
Esta función lee desde el fichero hasta que encuentra un carácter '\n' o hasta que lee longitud_max-1 caracteres y añade '\0' al final de la cadena. La cadena leída la almacena en buffer.
Si se encuentra EOF antes de leer ningún carácter o si se produce un error la función devuelve NULL, en caso contrario devuelve la dirección de buffer.
#include <stdio.h> int main() { FILE *fichero; char texto[100]; fichero=fopen("origen.txt","r"); if (fichero==NULL) { printf( "No se puede abrir el fichero.\n" ); exit( 1 ); } printf( "Contenido del fichero:\n" ); fgets(texto,100,fichero); while (feof(fichero)==0) { printf( "%s",texto ); fgets(texto,100,fichero); } if (fclose(fichero)!=0) printf( "Problemas al cerrar el fichero\n" ); }
Esta función la vamos a tratar en un tema posterior junto con la función fwrite. Estas dos funciones permiten guardar y leer cualquier tipo de dato, incluso estructuras.
© Gorka Urrutia