Otras funciones de E/S - Revisión de stdio.h






El archivo de encabezado stdio.h contiene, además de las familias de printf() y scanf() , otras funciones de gran utilidad; muy notablemente, aquí se encuentran las funciones de entrada/salida en disco. Aun cuando ciertamente trataremos el tema del acceso a disco en una sección dedicada exclusivamente a tan importante asunto, entendemos que es cómodo contar con una sección en que se hallarán una tras otra todas las funciones implicadas. No será necesario, de este modo recurrir a múltiples búsquedas en las páginas man . A continuación se muestra un listado de funciones con sus interfaces y significados, para después exponer algunos ejemplos.


void clearerr(FILE *flujo) Pone a cero el indicador de error del flujo proporcionado. Cuando se llega al final de un archivo, se activa (toma un valor no nulo) el indicador de fin de archivo. Entonces la función feof() aplicada al flujo en cuestión devuelve el valor verdadero. Este indicador de fin de archivo sólo vuelve a recibir el valor cero cuando se invoca a clearerr() . Véase también ferror() .
int fclose(FILE *flujo) Cierra la conexión entre el flujo y el archivo al que accedía. Si se utilizaba el flujo para escribir, se invoca a fflush() . La función proporciona un 0 si tiene éxito; si falla, produce EOF y se asigna un valor a la variable errno . Tanto si la función tiene éxito como si no, se deshace la relación entre el flujo y el archivo.
FILE * fdopen(int descriptor, char *modo) Asocia un flujo a un descriptor de archivo obtenido previamente. El descriptor de archivo se ha obtenido mediante una llamada a open() , dup() , create() o pipe() . Estas funciones abren el archivo, pero no proporcionan un puntero de FILE . El modo del descriptor tiene que coincidir con el modo del archivo.
int feof(FILE *flujo) Esta función devuelve un valor verdadero si el contador de archivo señala el último byte del archivo, lo cual se denota activando un indicador de fin de archivo, que es el que examina feof() . Para poner a cero el indicador de fin de archivo hay que emplear clearerr() .
int ferror(FILE *flujo) Esta función examina el indicador de error del flujo proporcionado, y devuelve un valor verdadero si este indicador está activado. El indicador de error sólo se puede poner a cero mediante clearerr() .
int fflush(FILE *flujo) Esta función desencadena la escritura del contenido del búfer asociado al flujo. No se modifica el estado de apertura. Si se proporciona un flujo NULL, se fuerza el volcado del búfer de todos los flujos que estén abiertos en modo de salida.
int fgetc(FILE *flujo) Proporciona el próximo carácter del flujo de entrada proporcionado, como carácter sin signo traducido a int. Desplaza el contador de archivo. Es una función equivalente a la macro getc().
char * fgetln(FILE *flujo, size_t *len) Esta función proporciona un puntero que señala el comienzo de la próxima línea del archivo asociado al flujo proporcionado. La longitud de la línea, incluyendo el retorno de carro, se proporciona en la variable señalada por len . Téngase en cuenta que en la última línea de un archivo puede no existir el carácter nueva línea, que por tanto no estará presente en la línea señalada.
int fgetpos(FILE *flujo, fpos_t *pos) Esta función equivale a ftell() . La función fgetpos() almacena en la variable señalada por pos el valor del contador de archivo en ese momento, esto es, indica en qué posición del archivo se va a leer o escribir el próximo carácter.
char * fgets(char *cad, int tamap, FILE *flujo) Esta función permite leer una línea del teclado, con la limitación de leer un máximo de tamaño-1 caracteres. Se añade el carácter '\0' al final de la cadena. Esta es, posiblemente, la función más adecuada para leer información de teclado, por cuanto permite garantizar la cantidad de información leída. Si el número de bytes leídos es menor que el espacio reservado en la cadena de destino, no podrá producirse un error por desbordamiento de ésta.
int fileno(FILE *flujo) Esta función proporciona un descriptor entero del flujo proporcionado. Cada flujo posee su número de descriptor, incluyendo stdin , stdout y stderr .
FILE * fopen(char *path, char *mode) Esta función abre el archivo cuyo nombre (y, opcionalmente, ruta) señala ruta , y le asocia un flujo. El parámetro de modo indica si se desea abrir el archivo para leer, escribir o leer y escribir; también se puede indicar nuestra intención de añadir información al archivo, así como otras combinaciones de opciones. Se hallará una descripción detallada de esta y otras funciones de tratamiento de archivos en la sección correspondiente .
int fprintf(FILE *flujo, const char *format, ...) Esta función permite escribir en un archivo de texto, efectuando las conversiones a formato alfanumérico que se le indiquen. Se hallará una descripción pormenorizada en la sección correspondiente .
int fpurge(FILE *flujo) Esta función descarta el contenido del búfer, tanto para flujos de entrada como para flujos de salida. Por tanto, si había en el búfer información sin volcar a disco (en un flujo de salida), una llamada a fpurge() hará que no llegue a escribirse en disco esa información. Si el archivo es de entrada ( stdin , por ejemplo) entonces se descarta el contenido del búfer, que queda vacío. Esta función es ideal para esta última aplicación, pues garantiza que no haya errores de lectura debidos a restos de cadenas que quedan en el búfer.
int fputc(int c, FILE *flujo) Esta función escribe el carácter c , convertido en " unsigned char ", en el flujo de salida proporcionado. La macro putc() es equivalente a esta función, aunque puede evaluar varias veces el flujo proporcionado; por tanto, para indicar el flujo no deben emplearse expresiones con efectos secundarios.. Finalmente, si se emplea fputc() con stdin como destino, el resultado es equivalente a una llamada a putchar() .
int fputs(const char *cad, FILE *flujo) Envía al flujo indicado la cadena cuyo puntero se da como primer argumento. Se añade un signo de nueva líne al final de la cadena.
size_t fread(void *ptro, size_t tamaño, size_t num_elementos, FILE *flujo) Esta función lee del flujo señalado por flujo num_elementos bloques de tamaño bytes de longitud, y los escribe en memoria a partir de la dirección dada por ptro . Como resultado, la función proporciona el número de bloques leídos. Si se produce un error, el resultado es un número de bloques menor que el esperado o cero. El final del archivo se detecta mediante una llamada a feof() . Esta función, al no hacer cambio de formato alguno, es la que permite leer de disco con mayor rapidez.
FILE * freopen(char *path, char *mode, FILE *flujo) Esta función sirve para reutilizar flujos creados anteriormente. Recibe como argumento el nombre (y, opcionalmente, la ruta) de un archivo, el modo de acceso (lectura o escritura) deseado para abrir el archivo, y el puntero de un flujo ya existente. El flujo, si existía anteriormente, se cierra, y se cambia el archivo asociado. Esta función suele utilizarse para cambiar cambiar el archivo asociado a los flujos de texto estánar ( stdin , stdout y stderr ).
FILE * fropen(void *cookie, int (*readfn)(void *, char *, int)) Véase funopen() . Esta función es análoga a funopen() ,pero permite especificar únicamente la nueva función de lectura.
int fscanf(FILE *stream, const char *format, ...) Esta función es análoga a scanf() , con una diferencia: en lugar de leer de stdin , lee del flujo proporcionado como primer argumento. Los demás parámetros tienen comportamiento idéntico al descrito en scanf() .
int fseek(FILE *stream, long desplazamiento, int desde_donde) Esta función permite especificar un valor para el contador de archivo correspondiente al flujo que se proporciona como primer parámetro. El segundo argumento es el número de byes que se desea desplazar el contador. El tercer argumento especifica el origen del desplazamiento especificado, y puede tomar los valores que se especifican a continuación:
SEEK_SET Desde el comienzo del archivo
SEEK_CUR Desde la posición actual del contador de archivo
SEEK_END Desde el final del archivo
De esta manera, la función fseek() se utilizará para situar el puntero de archivo donde convenga para leer o escribir. Por ejemplo,
La llamada... Significa...
fseek(fp, 0L, SEEK_SET); Situar el contador de archivo en el primer byte (equivale a rewind() ).
fseek(fp, 0L, SEEK_END); Situar el contador de archivo en el primer byte no utilizado, esto es, al final del archivo.
fseek(fp, (indice_elemento - 1)*longitud_elemento, SEEK_SET); Si se utilizan archivos formados por elementos de un cierto tipo cuyo tamaño en bytes es longitud_elemento , entonces esta expresión permite situar el contador de archivo en el elemento i-ésimo del mismo.
Como puede observarse, fseek() es clave para el manejo de archivos de acceso directo. La función, al igual que todas las de este tipo, proporciona el valor 0 si todo va bien y el valor -1 si se produce algún error.
int fsetpos(FILE *stream, const fpos_t *pos) Equivale a fseek() con un valor de desde_donde igual a SEEK_SET.
long ftell(FILE *flujo) Proporciona el valor del contador de posición de archivo correspondiente al flujo proporcionado. Es equivalente a fgetpos() .
FILE * funopen(void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int), fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *)) Esta función permite especificar nuevas funciones de apertura, lectura, escritura, posicionamiento y cierre para un nuevo flujo. Las funciones se proporcionan a través de punteros de función, y deben respetar las convenciones de fread(), fwrite(), lseek() y fclose(), respectivamente. El resultado podría ser, por ejemplo, un flujo utilizable en la forma habitual, en el cual el medio de escritura no fuera un disco, sino alguna zona de memoria. Obsérvese que en lugar del descriptor de archivos habitual en estas funciones, se les pasa un puntero llamado cookie, que contendrá información precisa para la operación. Esta es una función avanzada, que ofrece posibilidades interesantes a cierto nivel.
FILE * fwopen(void *cookie, int (*writefn)(void *, const char *, int)) Véase funopen() . Esta función es una variante que permite especificar únicamente la función de escritura sustitutiva.
size_t fwrite(const void *ptro, size_t tamaño, size_t num_elementos, FILE *flujo) Esta función escribe en el flujo señalado por flujo num_elementos bloques de tamaño bytes de longitud, leyéndolos de memoria a partir de la dirección dada por ptro . Como resultado, la función proporciona el número de bloques escritos. Si se produce un error, el resultado es un número de bloques menor que el esperado o cero. Esta función, al no hacer cambio de formato alguno, es la que escribe más rápidamente en disco.
int getc(FILE *flujo) Esto es una macro equivalente a fgetc(), que lee del flujo proporcionado un carácter, si existe.
int getchar() Equivale a getc(stdin) , se trata de una macro que proporciona el próximo carácter disponible en el búfer de teclado.
char * gets(char *cad) Esta función es peligrosa . La función gets() sirve para leer toda una línea del teclado, hasta el retorno de carro inclusive. Esto es útil pero plantea una dificultad grave: no permite limitar el número de caracteres leídos. Por tanto, es imposible garantizar que no se va a desbordar la capacidad reservada en la cadena de destino (la señalada por cad ), lo cual daría lugar a un fallo del programa. Por esta razón, se recomienda no utilizar gets() , especialmente cuando existen alternativar recomendables, como fgets() .
int getw(FILE *stream) Esta función permite leer el próximo int del flujo señalado por el parámetro que se proporciona.
int mkstemp(char *plantilla) Recibe un nombre de archivo (opcionalmente, con su ruta) a través del argumento. El nombre puede ser el de cualquier archivo, añadiéndole como extensión un cierto número de equis (" datos.0503 ", por ejemplo). La función devuelve un descriptor de ese nuevo archivo, que se habrá creado y estará abierto en modo de lectura y escritura. Entonces se puede emplear fdopen() para conseguir un FILE * que nos permitirá acceder al archivo recién creado. Esta función, junto con la siguiente, se declaran en el archivo de encabezado unistd.h .
int mktemp(char *plantilla) Es análoga a la función anterior, pero no llegar a crear el archivo. Es preferible la función anterior, que evita la pugna entre construir un nuevo nombre de archivo y verificar luego su existencia.Esta función, junto con la siguiente, se declaran en el archivo de encabezado unistd.h .
void perror(const char *string) Esta función imprime en pantalla el mensaje de error del sistema asociado al valor de la variable errno . Si el argumento no tiene el valor NULL , la cadena se antepone al mensaje de error, empleando ' : ' como separador.
int printf(const char *format, ...) Esta función es la encargada de imprimir en pantalla los valores de las variables que se proporcionan después de su primer argumento. El primera argumento es una cadena que debe contener tantos especificadores de formato como variables. Esta función se ha descrito en la sección correspondiente .
int puts(const char *cad) Esta función envía a stdout la cadena cuyo puntero se le proporciona, añadiendo un carácter de nueva línea al final de la misma.
int putchar(int c) Imprime en stdout el carácter que se le proporciona; equivale a putc() con stdout como argumento de salida.
int puts(const char *str) Esta función envía a stdout el contenido de la cadena aportada como argumento, añadiendo un carácter de nueva línea al final de la misma. Equivale a fputs() con un argumento de salida igual a stdout .
int putw(int w, FILE *stream) Esta función imprime la palabra (int) dada como primer argumento en el flujo que se indica. Esta función proporciona el valor 0 si tiene éxito, o bien EOF si se produce algún error de escritura (por ejemplo, cuando se intenta escribir en un flujo abierto para lectura).
int remove(const char *path) Esta función sirve para eliminar el directorio cuya ruta se le proporciona como argumento. Equivale a la llamada de sistema unlink() . La función remove() proporciona el valor 0 si concluye con éxito, o -1 en caso contrario. Si se produce un fallo (por las causas habituales al intentar eliminar un directorio), se asignará un valor a la variable global errno .
void rewind(FILE *stream) Esta función sirve para dar el valor cero al contador de archivo del flujo que se prorciona como argumento. Es equivalente a una llamada a fseek(fp, 0L, SEEK_SET) .
int scanf(const char *format, ...) Esta función se encarga de efectuar conversiones de formato según lo especificado en su primer argumento, almacenando los resultados en las variables cuyas direcciones se almacenan en la lista de argumentos posteriores. Se ha descrito en la sección correspondiente .
void setbuf(FILE *stream, char *buf) Esta función equivale a la siguiente llamada a setvbuf() :
setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
void setbuffer(FILE *stream, char *buf, size_t tamaño) Esta función es otra variante de setvbuf() ; en este caso, el tamaño del búfer depende de la elección el usuario, en lugar de emplear un tamaño de búfer BUFSIZ predeterminado, como sucedía en setbuf() .
int setlinebuf(FILE *stream) Esta función es una variante de setvbuf(), de la forma siguiente:
setvbuf(stream, (char *)NULL, _IOLBF, 0);
. Sirve para imponer un acoplamiento por líneas a un flujo.
int setvbuf(FILE *stream, char *buf, int modo, size_t tamaño) Las operaciones de entrada/salida de un flujo admiten tres modos de funcionamiento: sin memoria intermedia, por bloques o por líneas. Si no se usa memoria intermedia, la información enviada al fluo aparece inmediatamente en su destino. Si el funcionamiento es por bloques, los caracteres se almacenan en un bloque, que se envía a destino cuando se llena. Si el funcionamiento es por líneas, los caracteres se almacenan hasta que se escribe un carácter de nueva línea o se produce una lectura en el flujo.
Normalmente los archivos funcionan por bloques. La función setvbuf() sirve para modificar la configuracion de memoria importante de un flujo. El parámetro de modo admite estos tres valores posibles:
_IONBF Sin memoria intermedia
_IOLBF Por líneas
_IOFBF Por bloques, con acoplamiento completo.
El parámetro de tamaño debería ser cero, para que el sistema se encargue de reservar el bloque de memoria oportuno. Si el tamaño no es cero, entonces buf debería apuntar a un bloque de un mínimo de tamaño bytes de extensión, que se utilizará como memoria intermedia. Para evitar complicaciones y construir un código transportable, esta función debe invocarse una sola para el flujo en cuestión, y antes de realizar cualquier operación de entrada/salida.
int snprintf(char *str, size_t tamaño, const char *formato, ...) Esta función imprime un máximo de tamaño-1 bytes en la cadena indicada como primer argumento. El formato y las variables son, respectivamente, los argumentos tercero y siguientes. Proporciona como resultado el número de bytes escritos, sin incluir el '\0' final.
int sprintf(char *str, const char *format, ...) Esta función imprime su resultado en la cadena indicada como primer argumento. El formato y las variables son, respectivamente, los argumentos segundo y siguientes. Proporciona como resultado el número de bytes escritos, sin incluir el '\0' final.
int sscanf(const char *str, const char *format, ...) Esta función lee de la cadena indicada por su primer argumento, como si de un flujo se tratase. Va efectuando las conversiones de formato indicadas por el segundo argumento, y almacena los resultados en las variables cuyas direcciones constan en los argumentos tercero y siguientes. La lectura de cada variable se detiene al llegar al primer espacio o carácter que no pueda formar parte del tipo de conversión indicado. Esto significa, por ejemplo, que para sscanf() una frase es una colección de palabras; cada una iría a parar a una variable. El comportamiento de los indicadores de formato de scanf() es al análogo al de printf() (q.v.) .
char * strerror(int errnum) El prototipo de esta función pertenece al encabezado string.h . La función proporciona como resultado el puntero de la cadena de error del sistema correspondiente al número proporcionado como argumento. Estos valores dependen del idioma especificado mediante setlocale() .
sys_errlist Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
sys_nerr Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
tempnam Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
tmpfile Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
tmpnam Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
ungetc Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
vfprintf Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
vfscanf Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
vprintf Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
vscanf Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
vsnprintf Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
vsprintf Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.
vsscanf Consúltense las páginas man , y la disponibilidad de esta función en su sistema de desarrollo.


Ejercicios propuestos



  1. Ejercicio 0503r01.- Escribir un programa que muestre los números de descriptor de los flujos estándar ( stdin , stdout , stderr ).

  2. Ejercicio 0503r02.- Escribir un programa que utilice fgetln() para leer una línea de teclado sin posibilidad de desbordamiento, independientemente de la longitud de la línea leída.

  3. Ejercicio 0503r03.- Escribir un programa que utilice fgetln() para leer un archivo de texto, almacenando cada línea en una cadena de longitud mínima e igual a la longitud de la cadena en el archivo.

  4. Ejercicio 0503r04.- ¿Se puede contar el número de líneas del archivo empleando una orden de la shell? Revise las páginas man correspondientes a wc. Considere la posibilidad de modificar el programa anterior para obtener el número de líneas mediante un tubo, en la forma
    wc -l fichero.txt | leer fichero.txt
    suponiendo que leer sea el nombre del programa que hemos construido.

  5. Ejercicio 0503r05.- Se dispone de una estructura de datos de la forma siguiente:
    struct Registro {
    	char nombre[20], apellido_1[20], apellido_2[20];
    	char direccion[80];
    	float peso;
    	int talla;
    ;
    Se pide construir una función que reserve en disco espacio suficiente para N registros de este tipo, siendo N un argumento long que recibirá la función, junto con el nombre que debe darse al archivo. La función cerrará el archivo una vez creado. Nota: esto puede hacerse empleando N sentencias de escritura de tamaño sizeof(struct Registro) , o bien empleando 1 sentencia de escritura de tamaño N*sizeof(struct Registro) . Emplear ambos métodos y medir los tiempos invertidos.

  6. Ejercicio 0503r06.- Construir una función adecuada para leer el archivo creado en el ejercicio anterior. Emplear, igualmente el método basado en N lecturas y el método basado en 1 lectura. Medir ambos tiempos.

  7. Ejercicio 0503r07.- Mostrar en pantalla la lista de errores de un sistema, empleando sys_errlist[] y sys_nerr .

  8. Ejercicio 0503r08.- Escribir una función que reciba como argumento el nombre de un archivo de texto y proporcione como resultado una lista de punteros de char, cada uno de los cuales contendrá una línea del archivo.