Capitulo 6_Archivos

16
Capitulo 6 http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.] 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 práctica 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. Además, estaríamos 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. Además, estaríamos 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, leyéramos la salida de un programa y se la tecleáramos a otro. Es frecuente que la información de alguna compañía 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 información 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 diseñadas 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 física 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 términos de flujo de datos, algo parecido a canales a través 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 penúltimo. Existen varias funciones de acceso a archivos. Nosotros vamos a tratar con las siguientes, que son las más comunes: fopen(), fclose(), perror(), exit(). getc(), putc(). fgets(), fputs(). fprintf(), fscanf(). getw(), putw(). feof(), ferror(). fread(), fwrite(). fseek(), rewind(), ftell(). remove(), rename(). fflush(), ffushall(). 1. fopen(), fclose(), perror(), exit() Empezaremos por las más indispensables que son fopen() y fclose(). Esta pareja de funciones se usa para abrir y cerrar los flujos-archivo en un determinado programa. Esto es análogo al manejo de archivos en una oficina. Para manejar un archivo, primero hay que abrir el folder que lo contiene. Al terminar debemos de cerrarlo. La función fopen() abre los archivos para leer, escribir o añadir. El añadir implica escribir en el archivo preservando su contenido, es decir; se escribe al final del archivo. Los archivos siempre deben haber sido abiertos con fopen() antes de intentar accesarlos con cualquiera de las restantes funciones de entrada salida que tratamos en esta capitulo. Las funciones de E/S para archivos están definidas en el header stdio.h. El prototipo de fopen es:

description

Programar con archivos Salida y entrada

Transcript of Capitulo 6_Archivos

Capitulo 6

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

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. Sinembargo, en la práctica 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. Además,estaríamos 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. Además,estaríamos 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,leyéramos la salida de un programa y se la tecleáramos a otro.Es frecuente que la información de alguna compañía se almacene en la computadora. Los programas que la van a manejar deben deleerla 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 información en lo que se conoce como archivos. Un archivo es un conjunto dedatos 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 diseñadas para manejar archivos almacenados en disco bajo un nombrepropio.

1. Flujos

El manejo de archivos en C es mas eficiente que en otros lenguajes, ya que es independiente de la naturaleza física del archivo. Este puedeestar 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 términos de flujo de datos, algo parecido a canales a través de los cuales fluyen datos. Podemos desplazarnospor el flujo, corriente arriba o corriente abajo, buscando un bit en particular, o buscando el último bit o el penúltimo.

Existen varias funciones de acceso a archivos. Nosotros vamos a tratar con las siguientes, que son las más comunes:

fopen(), fclose(), perror(), exit().getc(), putc().fgets(), fputs().fprintf(), fscanf().getw(), putw().feof(), ferror().fread(), fwrite().fseek(), rewind(), ftell().remove(), rename().fflush(), ffushall().

1. fopen(), fclose(), perror(), exit()

Empezaremos por las más indispensables que son fopen() y fclose(). Esta pareja de funciones se usa para abrir y cerrar los flujos-archivo enun determinado programa. Esto es análogo al manejo de archivos en una oficina. Para manejar un archivo, primero hay que abrir el folder quelo contiene. Al terminar debemos de cerrarlo.

La función fopen() abre los archivos para leer, escribir o añadir. El añadir implica escribir en el archivo preservando su contenido, es decir; seescribe al final del archivo. Los archivos siempre deben haber sido abiertos con fopen() antes de intentar accesarlos con cualquiera de lasrestantes funciones de entrada salida que tratamos en esta capitulo.

Las funciones de E/S para archivos están definidas en el header stdio.h. El prototipo de fopen es:

Capitulo 6

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

FILE * fopen(char * nombre_archivo, * modo);

fopen() proporciona un apuntador a FILE. FILE es el nombre de una estructura definida en stdio.h. Esta estructura es empleada por las rutinasde E/S para almacenar información sobre el flujo-archivo una vez que ha sido abierto para lectura o escritura. El valor proporcionado porfopen() es un apuntador que identifica de forma unívoca al archivo. Una vez abierto, nunca nos volvemos a referir a él por su nombre. Enlugar de eso, lo identificaremos por el apuntador que fopen() nos haya proporcionado.

Por default, automáticamente, al trabajar en un programa en C, tenemos abiertos 3 flujos:

stdin: Entrada estándar, es decir, el teclado.stdout: Salida estándar, es decir, el monitor.stderr: Error estándar, 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 envían al monitor.

Algunos compiladores ofrecen otros flujos por default. Turbo C ofrece:

stdaux: Dispositivos auxiliares.stdprn: Impresora.

Estos flujos no forman parte del estándar ANSI, ya que los dispositivos como la impresora son muy variados.

La sintaxis de la función nos indica que fopen() toma argumentos: nombre_archivo y modo. Ambos son apuntadores acarácter,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 líneas. Cada línea esta constituida por una secuencia de caracteres ASCII yal final tiene el carácter salto de línea. Al final del archivo, se halla un carácter especial, el cual indica el fin del archivo. Este carácter serepresenta por la macro EOF, su valor depende del sistema operativo. Usualmente es un valor entero negativo. Para teclear este carácter se usapor lo regular la tecla CTRL seguida de otra. Por ejemplo en DOS se puede dar este carácter 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 archivosse hallan organizados como una secuencia lineal de bytes. Al final también tienen el carácter 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, además de los caracteres '\n' en cadalínea.Al guardar datos de tipo float o double, estos tienen que redondearse, con la consabida perdida de precisión.En modo binario los archivos no son legibles para el usuario, ya que se hallan en lenguaje de máquina.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 enmodo binario solo ocupa 2 bytes. En modo texto se escribe como una secuencia de caracteres ASCII, por lo que ocupa 5 bytes, ademásde los caracteres '\n' en cada línea.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 razón no hay perdida de precisión.

Capitulo 6

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

El modo puede ser una de las siguientes cadenas:

"r": Abre un archivo para lectura, en modo texto. Si el archivo no existe se genera un error."w": Abre un archivo y escribe datos en él, en modo texto . Si el archivo no existe, lo crea. Si ya existe ese archivo, su contenido actualse pierde."a": Abre un archivo para añadirle datos, en modo texto. Si el archivo no existe, se crea. Si ya existe ese archivo, su contenido actual sepreserva."rb": Abre un archivo para lectura, en modo binario. Si el archivo no existe se genera un error."wb": Abre un archivo y escribe datos en él, en modo binario . Si el archivo no existe, lo crea. Si ya existe ese archivo, su contenidoactual se pierde."ab": Crea y abre un archivo para añadirle datos, en modo binario, si el archivo no existe. Si ya existe ese archivo, su contenido actualse preserva."r+","w+": Abre un archivo para lectura-escritura, en modo texto."r+b", "w+b": Abre un archivo para lectura-escritura, en modo binario.

Como podemos observar los modos se especifican como cadenas y no como caracteres; esto se nota en el hecho de que se escriben entrecomillas.

El ejemplo siguiente muestra como se usa fopen() en un programa. En este fragmento de código , lo primero que hacemos es declarar dosapuntadores a flujo, archivo_ent y archivo_sal, que proporciona valores de tipo FILE *.

#include <stdio.h>

FILE * archivo_ent, * archivo_sal;

archivo_ent=fopen("entrada","r"); /* entrada esta abierto para lectura */

archivo_sal =fopen("salida","w"); /* salida esta abierto para escritura */

Una vez declarados los apuntadores, abrimos el archivo entrada para lectura y guardamos su apuntador bajo el nombre archivo_ent. De formaanáloga, abrimos el archivo salida para escritura y guardamos su apuntador bajo el nombre archivo_sal. Más adelante en el programa usaremossiempre los nombres de los apuntadores para referirnos a estos dos archivos

Puede ocurrir que no consigamos abrir dicho archivo. A lo mejor estamos pidiendo que se lea un archivo que no existe o para el que notenemos permiso de lectura; quizás estamos intentando crear un archivo que ya existe y no tengamos permiso para borrarlo; puede ser quehayamos excedido el número máximo de archivos que pueden estar abiertos a la vez en nuestro sistema, o pudiese ser que ya no hubieseespacio en disco. Siempre que, por cualquier razón, fopen() no puede abrir un archivo, regresa un apuntador NULL, que en stdio.h estadefinido así:

#define NULL ((void *)0)

Ya que el sistema operativo puede negarse a abrir un archivo, el programador prudente siempre comprueba los valores proporcionados porfopen().

Esto se realiza mediante las siguientes líneas 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

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

else if ((archivo_sal = fopen("chismes","w")) == NULL)

{

perror("no puedo abrir el archivo chismes\n");

exit(EXIT_FAILURE);

}

Con esto compruebas si en realidad se abre el archivo y estamos en disposición de poder trabajar con él.

La función perror() se usa para mandar el mensaje entre comillas a stderr. Además de esto también se escribe en stderr, el mensaje de errorgenerado por el sistema operativo. Esto es deseable, para la depuración. El inconveniente es que el mensaje de error, se halla en el idioma delsistema operativo. Esta función esta en stdio.h. Su prototipo es:

void perror(char * mensaje);

La función exit() termina la ejecución del programa, no importando en que punto de él nos encontremos. La macro EXIT_FAILURE, lausamos para indicar que el programa termino anormalmente. Si deseas indicar que todo termino bien empleas la macro EXIT_SUCCESS. Estafunción requiere el header stdlib.h. Su prototipo es:

void exit(int estado);

En los ejemplos que siguen, para mantener las cosas sencillas, no vamos a incluir la comprobación de errores, pero debes incluirla siempre.

Cuando ya no vamos a usar un archivo que hemos abierto con fopen(), deberíamos 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

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

:

/* 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 cierranautomáticamente siempre que se termina un programa en C, por lo que no resulta estrictamente necesario que cerremos explícitamente losarchivos. Tendremos que hacerlo, sin embargo, si vamos a abrir nuevamente el mismo archivo en un modo diferente, o si se da el caso de queexcedamos el número 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 sistemaque sirve para marcar el fin de archivo y está definido en stdio.h. También 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 carácter cada vez a unarchivo 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 históricos. La principal razón es que el carácter EOF, normalmente se manejacomo un int. Para demostrar el uso de getc() y putc(), el siguiente programa lee lo que introduzcamos por la terminal y lo escribe, carácter acarácter, en el archivo llamado temp; temp se cierra cuando introducimos el carácter EOF, que dependiendo el tipo de maquina puede sercontrol-D o bien control-Z. El programa vuelve a abrir automáticamente el archivo temp para lectura, luego lee y envía a nuestra terminal loque 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

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

return (0);

}

Este programa funciona de la siguiente manera :

Abrimos el archivo llamado temp solo para escritura, y después con el ciclo while se obtienen los caracteres de la entrada estándar yestamos checando que no sea el carácter de fin de archivo, cuando tecleemos dicho carácter se cierra el archivo, para posteriormenterealizar otro ciclo en el cual se debe de abrir el archivo para enviarlo a la salida estándar.Cuando getc() llega al final de un archivo, o cuando getc() o putc() encuentra alguna clase de error, proporcionara EOF. Es asuntonuestro 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 carácter de avance de línea o hasta que hayan sido leídoslongitud-1 bytes. Cuando se da una de estas condiciones, fgets() añade el carácter nulo '\0' a la cadena. Si la cadena termina con un avance delínea, este será incluido en la cadena y será agregado el carácter `\0´. Este comportamiento implica que siempre que se termine la línea a leer,la cadena contendría el carácter salto de línea. Tienes que tomar en cuenta esto al usar esta función.

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 carácter nulo en el extremo de la cadena, fputs() nunca añade una nuevalínea.

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

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

Si nosotros le introducimos el siguiente texto :

ahora es

el tiempo

para todos

los estudiantes

buenos

y las estudiantes

buenas

^z /* Fin de archivo en DOS */

/* la repite pero con la siguiente forma */

ahora esel

tiempopara

todoslos es

tudiantes b

uenos y las

estudiantes

buenas

Ahora si listamos el contenido del archivo tendremos:

ahora esel tiempoparatodoslos estudiantes buenos y lasestudiantesbuenas

¿ Te parece peculiar la salida ? El programa solo esta haciendo lo que le pedimos que haga. Puesto que no hay ningún espacio en blanco alfinal de ninguna de las líneas, nuestro texto esta almacenado en temp con las palabras es y el juntas y con las palabras tiempo y para en lamisma forma, como podemos constatar cuando listamos el archivo después de ejecutar el programa.

Leemos de nuevo el archivo, por medio de fgets con una cadena de doce caracteres de longitud. Pero dado que el archivo esta formado poruna sola línea larga, en cada llamada a fgets() se leen once caracteres cada vez, lo que produce el resultado que puede verse en el ejemplo. Apropósito, observa que aunque la última cadena no acaba con un avance de línea y no tiene once caracteres de longitud, fgets() la manejacorrectamente.

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 dela terminal. fprintf() tiene cierta utilidad como formateador de la salida para informes; fscanf() sirve para imponer una estructura a un archivode 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 número de caracteres enviados al archivo o, si da un error, un número negativo. El valor

Capitulo 6

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

proporcionado por fscanf() es el número de elementos que ha podido emparejar. Cuando fscanf() llega al final del archivo, proporciona unEOF.

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 dígitos y */

/* lo envía 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 dígitos, */ /* las convierte a enteros y las */ /* envía como dígitos 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 aenteros, 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 ponentantos bytes como sean necesario para formar una palabra o sea un entero. Este entero por supuesto variara dependiendo de la computadoradonde se ejecuta el programa. Por esta razón 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, también regresa EOF.

En este ejemplo puede verse a estas dos funciones en acción:

#include<stdio.h>

int main(void)

{

Capitulo 6

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

FILE * archivo;

int i;

archivo = fopen("temp","w"); /* abre el archivo para salida */

for (i=0;i<985;i+=123) /* envía 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 envía a stdout como dígitos */

return (0);

}

El punto importante del que hay que estar conscientes es que las funciones putw() y getw() nos permiten manejar enteros como enteros, sintener que convertirlos a cadenas de caracteres. Esta es una forma más eficiente de manejar enteros, pero es menos portable, ya que diferentesmáquinas tendrán diferentes tamaños de palabras y usaran ordenaciones de bytes diferentes para representar los valores enteros. Un archivoescrito por una máquina puede ser no legible por otra cuando se usan putw() y getw(), a no ser que se emplee una rutina de conversión parainterpretar los resultados. Por esta razón estas funciones no son parte del estándar ANSI.

1. feof(), ferror()

Una pregunta, ¿ si al leer de un archivo binario un carácter o un entero es posible que lo que leamos sea un valor tal que coincida con el deEOF?, ¿ podemos distinguir entre el valor de EOF de nuestro sistema y el valor entero que leímos ? Fácil; no podemos. Esto nos conduceinevitablemente a al discusión 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 dadocondiciones de fin-de-archivo o de error. Sus prototipos son:

int feof(FILE * apun_flujo);

int ferror(FILE * apun_flujo);

Cuando todo va bien, las dos proporcionan un cero. Cuando una función de E/S ha leído al final de un archivo, feof(apun_flujo) proporcionaEOF. Cuando se produce un error en la serie de apun_flujo, ferror(apun_flujo), proporciona un valor no nulo. Así que podíamos haber escritoestas líneas 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

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

if(feof(archivo)) /* comprobar la condición de EOF */

break; /* hemos llegado al final del archivo */

printf("%d\n",i);

}

De hecho, no estabamos en peligro para nada, puesto que ya sabíamos que nunca conseguiríamos un número negativo a partir de getw() amenos que hubiéramos llegado al final del archivo. Pero tal conocimiento es relativo a nuestro sistema. El uso de feof() hace que el programasea portable a otras máquinas.

ferror() es apropiada para checar si ha ocurrido algún error al manejar un archivo. Conviene si se desea saber el error ocurrido usarla encombinación con perror().

/* Se supone que ya se abrió el archivo para escribir en el */

while (!continuar) {

putc(dato,archi);

if (ferror(archi)) {

perror("\aHorror al manejar el archivo\n");

exit(EXIT_FAILURE);

}

. .

.

.

}

1. fread(), fwrite()

Para manejar archivos binarios, se usan 2 funciones. Las funciones fread() y fwrite(). Aunque los datos que manejen están orientados a bytes,estas funciones nos permiten agrupar los bytes en paquetes de distintos tamaños, dependiendo de que queramos interpretar luego los bytescomo 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);

Ambas funciones aceptan los mismos argumentos:

buffer es el lugar de memoria de donde de van a leer los datos o donde se van a escribir. Observa que este argumento esta declaradocomo void * buffer. Este argumento debe de poder manejar cualquier tipo de dato, y debe de pasarse como un apuntador ya que se va amodificar por fread(). Para lograr esto se maneja el apuntador a void. Este apuntador, lo introdujo el estándar ANSI y puede apuntar acualquier tipo de dato. Pero hay que pagar un precio: No se puede desreferenciar. Por esta razón debe de convertirse al tipo apropiadoantes de usarlo.longitud es el tamaño en bytes de los elementos de datos a leer y escribir .contador es el numero de elementos a leer y escribir por la llamada a la función. Cada elemento debe ser del tamaño especificado porlongitud.apun_flujo es el apuntador al archivo proporcionado por fopen().

Capitulo 6

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

Un ejemplo del uso de estas funciones lo da el siguiente código:

#include <stdio.h>

int main(void)

{

FILE * archi, * torombolo;

float 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);

}

En el programa se lee un flotante del archivo entrada y se escribe en el archivo salida. En ambos casos el buffer es la variable x. Usamos sizeofpara calcular el tamaño. Manejamos una sola variable. Al final se indica el apuntador al archivo pertinente.

Una de las ventajas que se tienen al manejar archivos binarios, es que ocupan menos memoria que los de texto. Pero hay otras que temostraremos a continuación, modificando el código anterior.

#include<stdio.h>

int main(void)

{

FILE * archi, * torombolo;

float x[1000];

archi = fopen("entrada","rb");

torombolo = fopen("salida","wb");

fread(x,sizeof(x),1,archi);

fwrite(x,sizeof(x[0]),1000,torombolo);

fclose(archivo);

fclose(torombolo);

return (0);

}

Ahora x no es una variable simple, sino un arreglo. En el fread, empleamos sizeof para calcular el tamaño del arreglo e indicamos que leemosun solo objeto de ese tamaño. En el fwrite obtenemos el tamaño de un elemento del arreglo y escribimos mil objetos de ese tamaño. La ventajade usar un archivo binario aquí, sobre un archivo de texto es que la lectura o escritura la hicimos en un solo paso. En modo texto se tendría queusar un ciclo, con el consabido gasto de mayor tiempo.

Capitulo 6

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

#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 deseáramos hacer esto en modo texto, se tendríaque manjar por separado una instrucción 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

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

fwrite(x,sizeof(x),1,torombolo);

fclose(archivo);

fclose(torombolo);

return (0);

}

En este caso si se quisiera usar modo texto se tendría que manejar por separado los campos y además 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 odirecto. Esto es, se puede leer o escribir en cualquier parte del archivo. Esto lo veremos con la siguientes funciones.

1. fseek(), rewind(), ftell()

Hasta ahora hemos tratado las funciones de E/S que leen y escriben archivos de principio a fin, es decir, secuencialmente, pero hay una formade eludir esta limitación. La función fseek() nos permite leer o escribir en cualquier punto de un archivo abierto por fopen(). Solo serecomienda su uso con archivos binarios, ya que en archivos de texto, la presencia de los '\n' puede producir errores al realizar losdesplazamientos. El prototipo de fseek() es:

int fseek(FILE * apun_flujo, long int offset,int modo);

El argumento de desplazamiento offset nos permite posicionarnos para la siguiente lectura o escritura, según sean los valores de offset y demodo:

Si el valor de modo es la macro SEEK_SET o 0, offset es el número de bytes desde el principio del archivo.Si el valor de modo es la macro SEEK_END o 2, offset es el número de bytes desde el final del archivo.Si el valor de modo es 1 o la macro SEEK_CURR o 1, offset es el número 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 atrás a voluntad. fseek() proporciona un cero a noser 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 atrás en nuestra terminal. Observa que el valor de offset es unlong:

#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

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

return (0);

}

¿ Por qué crees que hay un -2L en el segundo fseek() ? Cada vez que leemos un byte avanzamos una posición en el archivo, lo que significaque tenemos que retroceder dos veces para poder tomar el byte anterior. ¿ Por qué ponemos -2L ? Por que fseek() espera que en esa posiciónhaya un valor long, y no vamos a arriesgar a que no sea así.

La función rewind() se puede usar tanto con archivos binarios como de texto. Esta función solo nos ubica al principio del archivo. Si tieneéxito nos regresa 0.

La función ftell() es un complemento a fseek(). Esta función te dice donde estas, es decir, te regresa la posicion actual en el archivo, tomandocomo punto de referencia el principio del archivo. regresa -1L en caso de error. Su prototipo es:

long ftell(FILE * archivo);

Como ejemplo el siguiente programa copia un archivo al revés:

#include <stdio.h>

int main(int argc,char * argv[])

{

char entrada[81],salida[81];

long pos;

FILE * archi, * torombolo;

if (argc != 3) {

perror("Numero de argumentos invalido \n");

exit(EXIT_FAILURE);

}

archi = fopen(entrada, "rb"); /* abre el archivo para entrada */

torombolo = fopen(salida, "rb"); /* abre el archivo para salida */

fseek(archi, 1L, SEEK_END); /* busca el final del archivo */

pos = ftell(archi); /* Nos indica donde estamos */

while(pos)

{

putc(getc(archi),torombolo); /* copia un byte */

fseek(archi, -2l, SEEK_END);

pos = ftell(archi);

Capitulo 6

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

}

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 laposicion actual. Copiamos carácter a carácter el archivo de entrada en el de salida. Con fseek retrocedemos 2 bytes hacia atrás, ya que al leeravanzamos 1. Cuando ftell nos de 0, implica que estaremos al principio del archivo, con lo cual habremos terminado de copiar. La macroEXIT_SUCCESS nos indica que terminamos bien.

1. remove(), rename()

La función 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 función rename() cambia el nombre de un archivo, en caso de error regresa un valor distinto a 0. Su prototipo es:

int rename (char * nombre_viejo, char * nombre_nuevo);

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 salvación.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 algún otro, y cuando queremos leer algo, las funciones como scanf() toman lo que se hallaen el buffer. Análogamente también pasa esto al manejar archivos, ya que también se tienen un buffer por cada archivo abierto.

La finalidad de las funciones fflush() y flushall() es resetear los buffers. Sus prototipos son:

int fflush(FILE * archivo);

int flushall(void);

Si el flujo esta abierto para escritura, el buffer se vacía en el archivo. Si el buffer esta abierto para lectura, solo se vacía el buffer, es decir; sucontenido se pierde. Regresan cero si tienen éxito y algún valor distinto en caso de error. fflush() solo resetea un archivo. flushall() reseteatodos los flujos abiertos. Esta última función, no esta definida en el estándar ANSI. En el siguiente ejemplo se resetea el buffer después de

Capitulo 6

http://sai.uam.mx/apoyodidactico/pa/Unidad6/pauni6.html[03/03/2010 11:54:19 a.m.]

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.9fread(), fwrite() 6-156.10 fseek(), rewind(), ftell() 6-186.11 remove(), rename() 6-216.12 fflush(), flushall() 6-216.13 Autoevaluación 6-236.14 Índice 6-25