Академический Документы
Профессиональный Документы
Культура Документы
Capitulo 6
1.
Accesos a archivos.
1. Archivos.
Hasta el momento, cuando realizamos un programa, la entrada o salida (E/S), solo la hemos manejamos en el teclado y con el monitor. Sin
embargo, en la prctica pueden ocurrir los siguientes casos:
La cantidad de datos que requiere el programa, es tal, que no es practico introducirlos cada vez que se desee ejecutar. Adems,
estaramos muy propensos a cometer errores al teclearlos.
La cantidad de resultados que genera el programa, es tal, que no es practico intentar leerlos cada vez que se ejecuta. Adems,
estaramos muy propensos a que se nos pasara leer alguno de los resultados.
Algunos programas requieren que otros programas le manden sus resultados para poder trabajar. No seria practico, que nosotros,
leyramos la salida de un programa y se la tecleramos a otro.
Es frecuente que la informacin de alguna compaa se almacene en la computadora. Los programas que la van a manejar deben de
leerla directamente de ah.
El teclado y el monitor, no son los nicos dispositivos de E/S.
Por estas y otras razones, los programas deben leer y escribir informacin en lo que se conoce como archivos. Un archivo es un conjunto de
datos almacenado en memoria secundaria, que se referencia por un nombre. Este nombre depende del sistema operativo.
Las funciones de E/S con la que vamos a trabajar en esta parte han sido diseadas para manejar archivos almacenados en disco bajo un nombre
propio.
1. Flujos
El manejo de archivos en C es mas eficiente que en otros lenguajes, ya que es independiente de la naturaleza fsica del archivo. Este puede
estar en disco, cinta, ser la impresora, etc. En otros lenguajes se debe de considerar el tipo de dispositivo.
El C trata la E/S de archivos en trminos de flujo de datos, algo parecido a canales a travs de los cuales fluyen datos. Podemos desplazarnos
por el flujo, corriente arriba o corriente abajo, buscando un bit en particular, o buscando el ltimo bit o el penltimo.
Existen varias funciones de acceso a archivos. Nosotros vamos a tratar con las siguientes, que son las ms comunes:
fopen(), fclose(), perror(), exit().
getc(), putc().
fgets(), fputs().
fprintf(), fscanf().
getw(), putw().
feof(), ferror().
fread(), fwrite().
fseek(), rewind(), ftell().
remove(), rename().
fflush(), ffushall().
Capitulo 6
fopen() proporciona un apuntador a FILE. FILE es el nombre de una estructura definida en stdio.h. Esta estructura es empleada por las rutinas
de E/S para almacenar informacin sobre el flujo-archivo una vez que ha sido abierto para lectura o escritura. El valor proporcionado por
fopen() es un apuntador que identifica de forma unvoca al archivo. Una vez abierto, nunca nos volvemos a referir a l por su nombre. En
lugar de eso, lo identificaremos por el apuntador que fopen() nos haya proporcionado.
Por default, automticamente, al trabajar en un programa en C, tenemos abiertos 3 flujos:
stdin: Entrada estndar, es decir, el teclado.
stdout: Salida estndar, es decir, el monitor.
stderr: Error estndar, es decir, el monitor.
El flujo stderr se emplea para manejar los mensajes de error que se generan al ejecutar un programa. Por ejemplo al tratar de dividir entre 0.
Cuando esto ocurre, se genera un mensaje de error por parte del sistema operativo. Normalmente estos mensajes se envan al monitor.
Algunos compiladores ofrecen otros flujos por default. Turbo C ofrece:
stdaux: Dispositivos auxiliares.
stdprn: Impresora.
Estos flujos no forman parte del estndar ANSI, ya que los dispositivos como la impresora son muy variados.
La sintaxis de la funcin nos indica que fopen() toma argumentos: nombre_archivo y modo. Ambos son apuntadores a
carcter,nombre_archivo es el nombre del archivo que se desea abrir. Este nombre puede contener si se requiere el path, donde este el archivo.
Es importante notar que en este caso debes emplear \\ en vez de \ para dar el path.
Existen 2 formatos para manejar los archivos:
modo texto.
modo binario.
Un archivo de texto, consta de caracteres ASCII. Esta dividido en lneas. Cada lnea esta constituida por una secuencia de caracteres ASCII y
al final tiene el carcter salto de lnea. Al final del archivo, se halla un carcter especial, el cual indica el fin del archivo. Este carcter se
representa por la macro EOF, su valor depende del sistema operativo. Usualmente es un valor entero negativo. Para teclear este carcter se usa
por lo regular la tecla CTRL seguida de otra. Por ejemplo en DOS se puede dar este carcter con ctrl-Z y en UNIX con ctrl-D.
En un archivo binario los datos se manejan directamente como se almacenan en memoria, es decir como una secuencia de bits. Estos archivos
se hallan organizados como una secuencia lineal de bytes. Al final tambin tienen el carcter EOF. No se insertan caracteres '\n'.
Cada formato tiene ventajas y desventajas:
En modo texto los archivos son legibles para el usuario.
Se pueden crear y manejar con cualquier editor de texto.
Ocupan mucha memoria ya que cada dato se escribe como una secuencia de caracteres ASCII, adems de los caracteres '\n' en cada
lnea.
Al guardar datos de tipo float o double, estos tienen que redondearse, con la consabida perdida de precisin.
En modo binario los archivos no son legibles para el usuario, ya que se hallan en lenguaje de mquina.
Solo se pueden crear y manejar con un programa.
Ocupan menos memoria ya que cada dato solo ocupa los bytes que se requiere para representarlo. Por ejemplo se escribimos 32767 en
modo binario solo ocupa 2 bytes. En modo texto se escribe como una secuencia de caracteres ASCII, por lo que ocupa 5 bytes, adems
de los caracteres '\n' en cada lnea.
Al guardar datos de tipo float o double, estos no se tienen que redondear, ya que se almacenan tal y como se representan en la memoria.
Por esta razn no hay perdida de precisin.
Capitulo 6
Ya que el sistema operativo puede negarse a abrir un archivo, el programador prudente siempre comprueba los valores proporcionados por
fopen().
Esto se realiza mediante las siguientes lneas que deben incluirse en cada programa:
if((archivo_ent=fopen("secretos","r"))= =NULL)
{
perror("no puedo abrir el archivo secretos\n");
exit(EXIT_FAILURE);
}
Capitulo 6
La funcin exit() termina la ejecucin del programa, no importando en que punto de l nos encontremos. La macro EXIT_FAILURE, la
usamos para indicar que el programa termino anormalmente. Si deseas indicar que todo termino bien empleas la macro EXIT_SUCCESS. Esta
funcin requiere el header stdlib.h. Su prototipo es:
En los ejemplos que siguen, para mantener las cosas sencillas, no vamos a incluir la comprobacin de errores, pero debes incluirla siempre.
Cuando ya no vamos a usar un archivo que hemos abierto con fopen(), deberamos cerrarlo con fclose(). La sintaxis de fclose() es:
#include<stdio.h>
:
:
FILE * archivo_ent, * archivo_sal;
:
:
archivo_ent= fopen("secretos","r");
archivo_sal =fopen("chismes","w");
:
Capitulo 6
:
/* cuerpo del programa */
:
:
fclose(archivo_ent);
fclose(archivo_sal);
Al cerrar los archivos con fclose() se ponen en orden los buffers utilizados por el sistema. En realidad, todos los archivos se cierran
automticamente siempre que se termina un programa en C, por lo que no resulta estrictamente necesario que cerremos explcitamente los
archivos. Tendremos que hacerlo, sin embargo, si vamos a abrir nuevamente el mismo archivo en un modo diferente, o si se da el caso de que
excedamos el nmero de archivos que pueden estar abiertos en nuestro sistema a la vez.
Si fclose() logra cerrar un archivo, el valor regresado es un cero. Si no lo logra, proporcionar EOF, que es un valor dependiente del sistema
que sirve para marcar el fin de archivo y est definido en stdio.h. Tambin es prudente verificar el valor devuelto por fclose().
1. getc(), putc()
Como getchar y putchar, getc() y putc() son macros definidas en stdio.h. Lo que hace getc() y putc() es transferir un carcter cada vez a un
archivo que haya sido previamente abierto por fopen(). Pueden considerarse como funciones con los siguientes prototipos:
int getc(FILE * apun_flujo);
int putc(int c,FILE * apun_flujo);
Nota que ambas manejan int en vez de char. Esto es por motivos histricos. La principal razn es que el carcter EOF, normalmente se maneja
como un int. Para demostrar el uso de getc() y putc(), el siguiente programa lee lo que introduzcamos por la terminal y lo escribe, carcter a
carcter, en el archivo llamado temp; temp se cierra cuando introducimos el carcter EOF, que dependiendo el tipo de maquina puede ser
control-D o bien control-Z. El programa vuelve a abrir automticamente el archivo temp para lectura, luego lee y enva a nuestra terminal lo
que hayamos escrito en el archivo:
#include<stdio.h>
int main(void)
{
FILE * archivo;
int c;
archivo = fopen("temp","w");
while((c=getchar()) != EOF)
putc(c,archivo);
fclose(archivo);
archivo = fopen("temp","r");
while((c=getc(archivo)) != EOF)
putchar(c);
Capitulo 6
return (0);
}
Este programa funciona de la siguiente manera :
Abrimos el archivo llamado temp solo para escritura, y despus con el ciclo while se obtienen los caracteres de la entrada estndar y
estamos checando que no sea el carcter de fin de archivo, cuando tecleemos dicho carcter se cierra el archivo, para posteriormente
realizar otro ciclo en el cual se debe de abrir el archivo para enviarlo a la salida estndar.
Cuando getc() llega al final de un archivo, o cuando getc() o putc() encuentra alguna clase de error, proporcionara EOF. Es asunto
nuestro el procesamiento de las condiciones de error.
1. fgets(), fputs()
Las funciones fgets() y fputs() leen y escriben cadenas de caracteres. Se emplean con archivos de texto. Este es su prototipo:
char * fgets(char * cadena, int longitud,FILE * apun_flujo);
fgets() lee las cadenas de caracteres de un buffer de memoria, hasta que encuentra un carcter de avance de lnea o hasta que hayan sido ledos
longitud-1 bytes. Cuando se da una de estas condiciones, fgets() aade el carcter nulo '\0' a la cadena. Si la cadena termina con un avance de
lnea, este ser incluido en la cadena y ser agregado el carcter `\0. Este comportamiento implica que siempre que se termine la lnea a leer,
la cadena contendra el carcter salto de lnea. Tienes que tomar en cuenta esto al usar esta funcin.
Si se produce un error, fgets() regresa NULL. Y como siempre es nuestro deber checarlo.
En el caso de fputs(), proporciona una cadena de caracteres hasta, el carcter nulo en el extremo de la cadena, fputs() nunca aade una nueva
lnea.
Si se produce un error, fputs() proporciona EOF.
Ejemplo:
#include<stdio.h>
int main(void)
{
FILE * archivo;
char * cadena[80];
archivo = fopen("temp", "w"); /* abre archivo para salida */
while(gets(cadena) != NULL) /* obtener una cadena de stdin */
fputs(cadena, archivo); /* y enviarla a archivo */
fclose(archivo);
archivo = fopen("temp","r"); /* abre archivo para entrada */
while(fgets(cadena,12,archivo) !=NULL)/* obtener una cadena del archivo */
puts(cadena); /* y enviarla a stdout */
return (0);
}
Capitulo 6
1. fprintf() y fscanf()
Las funciones fprintf() y fscanf() se comportan como printf() y scanf(), excepto en que, por supuesto, leen y escriben en archivos en vez de
la terminal. fprintf() tiene cierta utilidad como formateador de la salida para informes; fscanf() sirve para imponer una estructura a un archivo
de entrada. Se usan para archivos de texto. Sus prototipos son:
int fprintf(FILE * apun_flujo, cadena_formato, arg1, arg2,..., argn);
int fscanf(FILE * apun_flujo, cadena_formato,argap1,argap2, ,argapn)
El valor proporcionado por fprinf() es el nmero de caracteres enviados al archivo o, si da un error, un nmero negativo. El valor
Capitulo 6
proporcionado por fscanf() es el nmero de elementos que ha podido emparejar. Cuando fscanf() llega al final del archivo, proporciona un
EOF.
Este es un ejemplo del funcionamiento de estas dos funciones:
#include <stdio.h>
int main(void)
{
FILE * archivo;
int i;
archivo = fopen("temp","w"); /* abre archivo para salida */
for (i=0;i<985;i+=123)
fprintf(archivo,"%03d",i); /* formatea i como 3 dgitos y */
/* lo enva al archivo */
fclose(archivo);
archivo = fopen("temp","r"); /* abrir archivo para entrada */
while(fscanf(archivo,"%03d",&i) !=EOF)
printf("%d\n",i); /* lee las cadenas de 3 dgitos, */ /* las convierte a enteros y las */ /* enva como dgitos a stdout */
return (0);
}
1. getw() y putw()
Las funciones que hemos explicado hasta ahora estaban orientadas hacia los caracteres getw() y putw(). Son dos funciones orientadas a
enteros, sus prototipos son:
int getw(FILE * apun_flujo);
int putw(int i,FILE * apun_flujo);
Estas funciones son variantes de nuestras viejas amigas getc() y putc(). En lugar de tomar o poner caracteres sencillos o bytes toman y ponen
tantos bytes como sean necesario para formar una palabra o sea un entero. Este entero por supuesto variara dependiendo de la computadora
donde se ejecuta el programa. Por esta razn estas funciones se emplean solo con archivos binarios.
getw() proporciona el siguiente entero de un archivo. Cuando llega al final del archivo, regresa EOF.
putw() proporciona el valor de su argumento. Si se produce un error, tambin regresa EOF.
En este ejemplo puede verse a estas dos funciones en accin:
#include<stdio.h>
int main(void)
{
Capitulo 6
FILE * archivo;
int i;
archivo = fopen("temp","w"); /* abre el archivo para salida */
for (i=0;i<985;i+=123) /* enva datos enteros al archivo */
putw(i,archivo);
fclose(archivo);
archivo = fopen("temp","r"); /* abre el archivo para entrada */
while((i=getw(archivo)) !=EOF) /* obtiene enteros del archivo */
printf("%d\n",i); /* y enva a stdout como dgitos */
return (0);
}
El punto importante del que hay que estar conscientes es que las funciones putw() y getw() nos permiten manejar enteros como enteros, sin
tener que convertirlos a cadenas de caracteres. Esta es una forma ms eficiente de manejar enteros, pero es menos portable, ya que diferentes
mquinas tendrn diferentes tamaos de palabras y usaran ordenaciones de bytes diferentes para representar los valores enteros. Un archivo
escrito por una mquina puede ser no legible por otra cuando se usan putw() y getw(), a no ser que se emplee una rutina de conversin para
interpretar los resultados. Por esta razn estas funciones no son parte del estndar ANSI.
1. feof(), ferror()
Una pregunta, si al leer de un archivo binario un carcter o un entero es posible que lo que leamos sea un valor tal que coincida con el de
EOF?, podemos distinguir entre el valor de EOF de nuestro sistema y el valor entero que lemos ? Fcil; no podemos. Esto nos conduce
inevitablemente a al discusin sobre las dos funciones de consulta de estado: feof() y ferror().
Estas dos funciones, macrofunciones en realidad, nos permiten consultar el estado de un determinado flujo de E/S para ver si se han dado
condiciones de fin-de-archivo o de error. Sus prototipos son:
Cuando todo va bien, las dos proporcionan un cero. Cuando una funcin de E/S ha ledo al final de un archivo, feof(apun_flujo) proporciona
EOF. Cuando se produce un error en la serie de apun_flujo, ferror(apun_flujo), proporciona un valor no nulo. As que podamos haber escrito
estas lneas a partir de nuestro ltimo ejemplo, de la manera siguiente:
while((i=getw(archivo)) !=EOF)
printf("%d\n",i);
for(;;) /* ciclo sin fin */
{
i= getw(archivo); /* obtener el entero siguiente */
Capitulo 6
1. fread(), fwrite()
Para manejar archivos binarios, se usan 2 funciones. Las funciones fread() y fwrite(). Aunque los datos que manejen estn orientados a bytes,
estas funciones nos permiten agrupar los bytes en paquetes de distintos tamaos, dependiendo de que queramos interpretar luego los bytes
como enteros, como caracteres, como variables estructurada, o como queramos hacerlo. Estas funciones tienen los siguientes prototipos:
int fread(void * buffer, int longitud, int contador,FILE * apun_flujo);
int fwrite(void * buffer, int longitud, int contador,FILE * apun_flujo);
Capitulo 6
Capitulo 6
#include<stdio.h>
typedef struct Hoja {
char nombre[20], domicilio[40], telefono[20];
int edad;
char sexo;
} hoja;
int main(void)
{
FILE * archi, * torombolo;
hoja x;
archi = fopen("entrada","rb");
torombolo = fopen("salida","wb");
fread(&x,sizeof(x),1,archi);
fwrite(&x,sizeof(x),1,torombolo);
fclose(archivo);
fclose(torombolo);
return (0);
}
Es este caso x es una estructura. Se esta manejando en un solo paso la lectura y la escritura. Si deseramos hacer esto en modo texto, se tendra
que manjar por separado una instruccin con cada campo de la estructura.
#include<stdio.h>
typedef struct Hoja {
char nombre[20], domicilio[40], telefono[20];
int edad;
char sexo;
} hoja;
int main(void)
{
FILE * archi, * torombolo;
hoja x[500];
archi = fopen("entrada","rb");
torombolo = fopen("salida","wb");
fread(x,sizeof(x[0]),500,archi);
Capitulo 6
fwrite(x,sizeof(x),1,torombolo);
fclose(archivo);
fclose(torombolo);
return (0);
}
En este caso si se quisiera usar modo texto se tendra que manejar por separado los campos y adems usar ciclos.
Una ventaja adicional de los archivos binarios, es el manejar tipos de datos complejos en un solo paso. Otra ventaja es el acceso aleatorio o
directo. Esto es, se puede leer o escribir en cualquier parte del archivo. Esto lo veremos con la siguientes funciones.
El argumento de desplazamiento offset nos permite posicionarnos para la siguiente lectura o escritura, segn sean los valores de offset y de
modo:
Si el valor de modo es la macro SEEK_SET o 0, offset es el nmero de bytes desde el principio del archivo.
Si el valor de modo es la macro SEEK_END o 2, offset es el nmero de bytes desde el final del archivo.
Si el valor de modo es 1 o la macro SEEK_CURR o 1, offset es el nmero de bytes con respecto del byte actual dentro del archivo.
Puesto que el valor de offset puede ser negativo, podemos movernos hacia adelante y hacia atrs a voluntad. fseek() proporciona un cero a no
ser que intentemos poner o tomar un valor fuera de los limites del archivo.
Como ejemplo, el siguiente programa abre el archivo test.c y lo imprime hacia atrs en nuestra terminal. Observa que el valor de offset es un
long:
#include<stdio.h>
int main(void)
{
FILE * archivo;
archivo = fopen("test.c","rb"); /* abre el archivo para entrada */
fseek(archivo,0L,2); /* busca el final del archivo */
do
{
putchar(getc(archivo)); /* imprime el siguiente ciclo */
}
while(!fseek(archivo,-2L,1));
Capitulo 6
return (0);
}
Por qu crees que hay un -2L en el segundo fseek() ? Cada vez que leemos un byte avanzamos una posicin en el archivo, lo que significa
que tenemos que retroceder dos veces para poder tomar el byte anterior. Por qu ponemos -2L ? Por que fseek() espera que en esa posicin
haya un valor long, y no vamos a arriesgar a que no sea as.
La funcin rewind() se puede usar tanto con archivos binarios como de texto. Esta funcin solo nos ubica al principio del archivo. Si tiene
xito nos regresa 0.
La funcin ftell() es un complemento a fseek(). Esta funcin te dice donde estas, es decir, te regresa la posicion actual en el archivo, tomando
como punto de referencia el principio del archivo. regresa -1L en caso de error. Su prototipo es:
Capitulo 6
}
putc(getc(archi),torombolo); /* copia el ultimo byte */
fclose(archi);
fclose(torombolo);
exit(EXIT_SUCCESS);
}
Por supuesto falta validar los resultados de fopen y de fclose. Con fseek nos posicionamos al final del archivo de entrada. ftell nos da la
posicion actual. Copiamos carcter a carcter el archivo de entrada en el de salida. Con fseek retrocedemos 2 bytes hacia atrs, ya que al leer
avanzamos 1. Cuando ftell nos de 0, implica que estaremos al principio del archivo, con lo cual habremos terminado de copiar. La macro
EXIT_SUCCESS nos indica que terminamos bien.
1. remove(), rename()
La funcin remove() borra un archivo. Su prototipo es:
int remove(char * archivo);
En caso de error regresa un valor distinto a 0. Si es necesario el nombre del archivo puede incluir un path.
La funcin rename() cambia el nombre de un archivo, en caso de error regresa un valor distinto a 0. Su prototipo es:
1. fflush(), flushall()
Te has topado con problemas al leer usando scanf() o getchar() ? Si es as las funciones fflush() y flushall(), pueden ser tu salvacin.
Cuando leemos datos del teclado, realmente no se van del teclado a las variables que estemos manejando, si no que los datos se van a un buffer.
De este buffer es donde las funciones como scanf() o getchar() toman sus valores.
A veces al trabajar, damos caracteres '\r' de mas o algn otro, y cuando queremos leer algo, las funciones como scanf() toman lo que se halla
en el buffer. Anlogamente tambin pasa esto al manejar archivos, ya que tambin se tienen un buffer por cada archivo abierto.
La finalidad de las funciones fflush() y flushall() es resetear los buffers. Sus prototipos son:
Si el flujo esta abierto para escritura, el buffer se vaca en el archivo. Si el buffer esta abierto para lectura, solo se vaca el buffer, es decir; su
contenido se pierde. Regresan cero si tienen xito y algn valor distinto en caso de error. fflush() solo resetea un archivo. flushall() resetea
todos los flujos abiertos. Esta ltima funcin, no esta definida en el estndar ANSI. En el siguiente ejemplo se resetea el buffer despus de
Capitulo 6
leer.
/* Se presupone que ya se abri el archivo */
c=getchar();
fflush();
1. ndice
6. ACCESOS A ARCHIVOS 6-16.1 Archivos. 6-16.2 Flujos 6-26.3 fopen(), fclose(), perror(), exit() 6-26.4 getc(),
putc() 6-86.5 fgets(), fputs() 6-96.6 fprintf() y fscanf() 6-116.7 getw(), putw() 6-126.8 feof(), ferror() 6-136.9
fread(), fwrite() 6-156.10 fseek(), rewind(), ftell() 6-186.11 remove(), rename() 6-216.12 fflush(), flushall() 6216.13 Autoevaluacin 6-236.14 ndice 6-25