Tema 10 - Entrada y salida.
Antes de empezar a explicar la entrada y salida en C, es necesario realizar dos
pequeños comentarios:
En primer lugar, para el correcto funcionamiento de la entrada y salida en C, y
dado que las funciones de E/S, estructuras de datos usadas por esas funciones, etc., se
encuentran declaradas en el archivo de cabecera <stdio.h>, es necesario incluir dicho
archivo, mediante la directiva del preprocesador #include, para que la E/S funcione
correctamente, pues en caso contrario, puede funcionar de forma incorrecta, e incluso,
puede llegar a dar errores de compilación.
En segundo lugar, aparte de la E/S por consola y la E/S de fichero mediante
buffer intermedio, que serán explicadas en este tema, existe una E/S de fichero sin
buffer intermedio, proveniente de la primitiva implementación de C en máquinas
UNIX., y que el standard ANSI de C no ha estandarizado, por lo cual, no es
recomendable su uso. Por este motivo, y dada su similitud en la mayoría de apartados
con el sistema de E/S de fichero mediante buffer intermedio, no será explicado en el
presente tema.
10.1 - Entrada y salida desde consola.
La entrada y salida desde consola se refiere a las operaciones que se producen
en el teclado y la pantalla del ordenador. Dichos dispositivos son automáticamente
abiertos y cerrados al comenzar y terminar el programa, por lo cual, no deben ser
abiertos ni cerrados por el propio programa. Existen, básicamente, seis funciones de
entrada y salida desde consola, tres de entrada y tres de salida. Veámoslas:
La función getchar(), lee un carácter desde el teclado. Dicha función se define
como:
int getchar(void);
Dicha función lee caracteres, de uno en uno, desde el teclado, esperando, para
leer los caracteres, la pulsación de un retorno de carro. Es por ello que es posible
escribir varios caracteres antes de que se ninguno de ellos sea leído. La función
getchar() hace eco en pantalla del carácter leído. En caso de error devuelve EOF.
La función putchar() escribe un carácter a la pantalla del ordenador. Dicha
función se define como:
int putchar(int c);
La función putchar(), si sucede de forma correcta, devuelve el carácter escrito.
En caso de error devuelve el carácter EOF.
Veamos un ejemplo de uso de getchar() y putchar():El lenguaje de programación C
#include <stdio.h>
int main(void)
{
char ch;
do
{
ch=getchar();
putchar(ch);
}
while (ch!='e' && ch!='E');
return 0;
}
Este programa lee todas las teclas pulsadas en el teclado, y las coloca en
pantalla, hasta leer una 'e' o una 'E'. Obsérvese que solo lee las teclas después de pulsar
un retorno de carro.
La función gets() lee un string desde el teclado. La función se define como:
char *gets(char *s);
La función gets() lee un string desde el teclado hasta que se pulsa un retorno de
carro. El string es almacenado en la variable s, y el retorno de carro leído desde el
teclado es, automáticamente, reemplazado por un carácter de final de string ('\0').
Devuelve un puntero a la variable s si sucede de forma correcta, y NULL en caso
contrario. La función gets() permite corregir errores de teclado usando la tecla de
retroceso antes de pulsar el retorno de carro.
La función puts() escribe un string en pantalla. La función se define como:
int puts(const char *s);
La función puts() escribe en pantalla el string almacenado en s, y añade al final
un retorno de carro. Devuelve un entero no negativo si sucede de forma correcta, y
EOF en caso de error.
Veamos un ejemplo de uso de gets() y puts():
#include <stdio.h>
#define TAM 100
int main(void)
{
char cadena[TAM];
puts("Introduce una cadena:");
gets(cadena);
return 0;
}El lenguaje de programación C
La función scanf() se usa para leer cualquier tipo de dato predefinido desde el
teclado, y convertirlo, de forma automática, al formato interno adecuado. La función se
define como:
int scanf(const char *formato[,dirección,...]);
El string formato es la cadena de control que indica los datos a leer. Dicha
cadena de control consta de tres clases de caracteres:
• Especificadores de formato.
• Caracteres de espacio en blanco.
• Caracteres que no sean espacios en blanco.
Los especificadores de formato están precedidos por el signo %, y dicen a la
función que tipo de datos van a ser leídos a continuación. Los especificadores de
formato validos son:
Especificado
r
Descripción.
%c Leer un único carácter.
%d Leer un entero decimal.
%i Leer un entero decimal.
%e Leer un número en punto flotante.
%f Leer un número en punto flotante.
%g Leer un número en punto flotante.
%o Leer un número octal.
%s Leer una cadena de caracteres.
%x Leer un número hexadecimal.
%p Leer un puntero.
%n Recibe un valor igual al número de carácter leídos.
%u Leer un entero sin signo.
Tabla 10.1.1: Especificadores de formato de la función scanf().
Además, es posible utilizar los modificadores h (short), l (long) y L. El
modificador h se puede aplicar a los tipo d, i, o, u y x, e indica que el tipo de dato es
short int o unsigned short int según el caso. El modificador l se puede aplicar a los
casos anteriores, indicando que el tipo de dato es long int o unsigned long int, pero,
además, se puede aplicar a los tipos e, f y g, indicando, en tal caso, que el tipo de dato
es double. Por último, el modificador L se puede aplicar a los tipos e, f y g, e indica que
el tipo de dato es long double.
Los caracteres de espacio en blanco en la cadena de control dan lugar a que
scanf() lea y salte sobre cualquier número (incluido cero) de espacios en blanco. Un
espacio en blanco es, además del carácter espacio, un tabulador o un salto de línea.
Un carácter que no sea espacio en blanco da lugar a que scanf() lea y elimine el
carácter asociado. Por ejemplo, %d:%d da lugar a que scanf() lea primero un int,El lenguaje de programación C
después lea, y descarte, los dos puntos, y luego lea otro int. Si el carácter especificado
no se encuentra, scanf() termina su ejecución.
Todas las variables utilizadas para recibir valores (si son necesarias), deben ser
pasadas por "referencia", o sea, por sus direcciones. Esto supone que los argumentos
deben ser punteros a las variables.
La presencia del signo * después del signo % y antes del código del formato
produce que scanf() lea, pero no asigne el valor leído a ninguna variable. Por ejemplo:
int x,y;
scanf("%d%*c%d",&x,&y);
Provoca que, si la entrada es 10/20, se le asigne el valor 10 a la variable x, se
lea, y se descarte el signo /, y después se asigne el valor 20 a la variable y.
La función scanf() devuelve un número igual al de campos que han sido
asignados correctamente, este número no incluye los campos que fueron leídos, pero
no asignados, utilizando el modificador * para eliminar la asignación. En caso de error
devuelve EOF.
La función printf() se usa para escribir cualquier tipo de dato a la pantalla. Su
formato es:
int printf(const char *formato[,argumento,...]);
La cadena apuntada por formato consta de dos tipos de elementos. El primer tipo
esta constituido por los caracteres que se mostraran en pantalla. El segundo tipo
contiene las ordenes de formato que describen la forma en que se muestran los
argumentos. Las ordenes de formato están precedidas por el signo % y le sigue el
código de formato. Estas ordenes de formato son:
Especificado
r
Descripción
%c Carácter.
%d Enteros decimales con signo.
%i Enteros decimales con signo.
%e Punto flotante en notación científica (e minúscula).
%E Punto flotante en notación científica (E mayúscula).
%f Punto flotante.
%g Usar el más corto de %e y %f.
%G Usar el más corto de %E y %f.
%o Octal sin signo.
%s Cadena de caracteres.
%u Enteros decimales sin signo.
%x Hexadecimales sin signo (letras minúsculas).
%X Hexadecimales sin signo (letras mayúsculas).
%p Mostrar un puntero.El lenguaje de programación C
%n El argumento asociado es un puntero a un entero, el cual recibirá el
número de caracteres escritos.
%% Imprimir el signo %.
Tabla 10.1.2: Especificadores de formato de la función printf().
Además, e igual que con la función scanf(), existen los modificadores h, l y L.
Para su uso consultar la función scanf().
La función printf() devuelve el número de carácteres escritos. En caso de error
devuelve el valor EOF.
Veamos un ejemplo de uso de las funciones scanf() y printf():
#include <stdio.h>
int main(void)
{
int a,b;
printf("\nIntroduce el valor de a: ");
scanf("%d",&a);
printf("\nIntroduce el valor de b: ");
scanf("%d",&b);
if (b!=0)
printf("\nEl valor de %d dividido %d es: %f\n",
a,b,(float)a/b);
else
printf("\nError, b vale 0\n");
return 0;
}
10.2 - Entrada y salida desde fichero.
Antes de explicar la entrada y salida desde fichero, conviene explicar el tipo de
dato FILE *. Dicho tipo de dato es el "puntero de fichero", y es, realmente, una
estructura que contiene la información sobre el nombre del fichero abierto, su modo de
apertura (lectura, escritura, etc.), estado, etc. Dicho "puntero de fichero", por tanto,
especifica el fichero que se esta usando y es, la forma que poseen las funciones de
entrada y salida desde fichero de conocer sobre que archivo actúan.
Sobre un archivo es necesario, antes de poder usarlo, realizar una operación, la
apertura del mismo; una vez terminado su uso, es necesaria otra operación, cerrar el
archivo. De esto se encargan dos funciones de C. Dichas funciones son fopen() y
fclose(). Veámoslas con detalle:
La función fopen() se encarga de abrir un archivo. Su definición es:
FILE *fopen(char *nombre,char *modo);El lenguaje de programación C
Donde nombre es un string que contiene el nombre del archivo que queremos leer
y modo es otro string que contiene el modo de apertura deseado. Dichos modos de
apertura son:
Modo Descripción
r Abrir un archivo para lectura.
w Crear un archivo para escritura.
a Abrir un archivo para añadir.
rb Abrir un archivo binario para lectura.
wb Crear un archivo binario para escritura.
ab Abrir un archivo binario para añadir.
rt Abrir un archivo de texto para lectura.
wt Crear un archivo de texto para escritura.
at Abrir un archivo de texto para añadir.
r+ Abrir una archivo para lectura/escritura.
w+ Crear un archivo para lectura/escritura.
a+ Abrir un archivo para leer/añadir.
r+b Abrir un archivo binario para lectura/escritura.
w+b Crear un archivo binario para lectura/escritura.
a+b Abrir un archivo binario para leer/añadir.
r+t Abrir un archivo de texto para lectura/escritura.
w+t Crear un archivo de texto para lectura/escritura.
a+t Abrir un archivo de texto para leer/añadir.
Tabla 10.2.1: Modos de apertura de un fichero con la función fopen().
En todos los casos de añadir, si el archivo especificado no existe, se procede a
crearlo.
Si no se especifica en modo si la apertura se realiza para un archivo binario o
texto, dependerá de la configuración del sistema que la apertura sea en binario o en
texto, siendo en la mayoría de los casos en modo texto. La diferencia fundamental
entre modo texto y modo binario es que en modo texto, secuencias de lectura tales
como retorno de carro/alimentación de línea se traducen en un único carácter nueva
línea, mientras que en modo texto eso no sucede; el efecto contrario sucede en
escritura.
La función fopen() devuelve un puntero de tipo FILE a la estructura que
representa el archivo abierto. En caso de que no pueda abrir o crear el archivo
especificado, se devuelve un puntero NULL, por lo cual, siempre que se abra un
archivo, deberá comprobarse que el valor devuelto no es NULL, y entonces, el código
deberá ser:
FILE *fp;
if ((fp=fopen("prueba","w"))==NULL)
{
puts("\nNo puedo abrir el fichero\n");
exit(1);
}El lenguaje de programación C
La función fclose() cierra un archivo. Su definición es:
int fclose(FILE *fp);
Donde fp es el puntero al fichero abierto mediante la función fopen().
La función fclose() cierra el archivo, lo cual da lugar a que el buffer de archivo
existente en memoria se libere, escribiéndose en el fichero si es necesario, además,
libera el bloque de control de archivo, lo cual lo hace disponible para otro archivo (el
sistema operativo limita el número de ficheros abiertos simultáneamente).
Un valor devuelto de cero indica que el archivo fue cerrado con éxito.
Cualquier valor distinto de cero indica un error.
Veamos un ejemplo de uso de fopen() y fclose():
#include <stdio.h>
int main(int argc,char *argv[])
{
FILE *fp;
if (argc!=2)
{
puts("Nombre del fichero no pasado");
return 0;
}
if ((fp=fopen(argv[1],"r"))==NULL)
{
printf("Error abriendo el fichero: %s\n",argv[1]);
return 0;
}
if (fclose(fp))
{
puts("Error cerrando el fichero");
return 1;
}
return 0;
}
Una vez abierto un archivo, y hasta que se proceda a cerrarlo es posible leer,
escribir, etc., en el, según se indique en el modo de apertura. Las principales funciones
de lectura y escritura sobre un archivo son:
int getc(FILE *fp);
int putc(int ch,FILE *fp);
char *fgets(char *str,int n,FILE *fp);
int fputs(const char *str,FILE *fp);
int fscanf(FILE *fp,const char *formato[,dirección,...]);
int fprintf(FILE *fp,const char *formato[,argumento,...]);
int fread(void *memoria,int num,int cont,FILE *fp);
int fwrite(void *memoria,int num,int cont,FILE *fp);El lenguaje de programación C
La función getc() lee caracteres del archivo asociado a fp. Devuelve EOF
cuando se alcanza el final del archivo.
La función putc() escribe el carácter ch en el archivo asociado a fp. Devuelve el
carácter escrito si funciona de forma correcta, y EOF en caso de error.
La función fgets() funciona de igual forma que la función gets(), solo que,
además de leer del fichero asociado a fp, el parámetro n indica el número máximo de
caracteres que se pueden leer. Existe, además, una sutil diferencia, la función fgets() no
elimina el retorno de carro (si se lee) de la cadena de entrada, sino que lo conserva en
la misma,. añadiendo a continuación de dicho retorno de carro, y de forma automática,
el carácter de fin de cadena ('\0').
La función fputs() funciona igual que la función puts(), solo que, además de
escribir en el fichero asociado a fp, no añade al final del string un retorno de carro, tal
y como hacia la función puts().
Las funciones fscanf() y fprintf() funcionan de forma similar a sus equivalentes
sobre la consola scanf() y printf(), solo que leen o escriben del archivo asociado a fp.
Las función fread() permite leer un bloque de datos. Su declaración es:
int fread(void *memoria,int num,int cont,FILE *fp);
Donde memoria es un puntero a la zona de memoria donde se almacenaran los
datos leídos, num es el tamaño (en bytes) de cada uno de los bloques a leer, cont es el
número de bloques (cada uno de num bytes de tamaño) a leer, y fp es el puntero al
fichero desde donde se lee.
La función fread() devuelve el número de bloques (no bytes) realmente leídos.
La función fwrite() es la función dual a fread(). La función fwrite() permite
escribir un bloque de datos. Su declaración es:
int fwrite(void *memoria,int num,int cont,FILE *fp);
Donde memoria es un puntero a la zona de memoria donde se encuentran los
datos a escribir, num es el tamaño (en bytes) de cada uno de los bloques a escribir,
cont es el número de bloques (cada uno de num bytes de tamaño) a escribir, y fp es el
puntero al fichero desde donde se escribe.
La función fwrite() devuelve el número de bloques (no bytes) realmente
escritos.
Un aspecto a resaltar de las funciones fread() y fwrite() es el hecho de que no
realizan ningún tipo de conversión con los datos leídos o escritos, así , la secuencia
retorno de carro/alimentación de línea, no es convertida en el carácter nueva línea en la
escritura, y viceversa para la lectura. Es por ello, que dichas funciones son,
generalmente, usadas con archivos abiertos en modo binario.El lenguaje de programación C
Veamos un ejemplo de uso de fread() y fwrite():
#include <stdio.h>
#define TAM 1000
int main(int argc,char *argv[])
{
FILE *f_inp,*f_out;
char buffer[TAM];
int num;
if (argc!=3)
return 0;
if ((f_inp=fopen(argv[1],"rb"))==NULL)
return 0;
if ((f_out=fopen(argv[2],"wb"))==NULL)
exit(1);
while ((num=fread(buffer,sizeof(char),TAM,f_inp))!=0)
fwrite(buffer,sizeof(char),num,f_out);
if (fclose(f_inp) || fclose(f_out)
exit(1);
return 0;
}
Además de las funciones de entrada y salida de datos desde archivo, descritas
con anterioridad, existen tres funciones que no son de entrada y salida de datos y que
conviene explicar. Dichas funciones son:
int ferror(FILE *fp);
void rewind(FILE *fp);
int fseek(FILE *fp,long num,int origen);
La función ferror() devuelve si durante la última operación realizada sobre el
archivo asociado a fp se produjo o no un error. Devuelve el valor cero si no se produjo
error, y un valor distinto de cero si se produjo error.
La función rewind() posiciona el indicador de posición del archivo fp al
principio del mismo.
La función fseek() se usa para operaciones de entrada y salida de acceso
aleatorio. La función fseek() desplaza el indicador de posición del archivo fp un tamaño
num desde la posición especificada por origen. Los valores validos para origen son:
Origen Nombre de la constante Valor
Comienzo del archivo SEEK_SET 0
Posición actual SEEK_CUR 1
Final del archivo SEEK_END 2
Tabla 10.2.2: Valores del origen en la función fseek().El lenguaje de programación C
La función fseek() devuelve un valor de cero si funciona correctamente. Un
valor distinto de cero indica un error en la última operación de posicionamiento en el
fichero.
La función fseek() solo funciona correctamente en archivos abiertos en modo
binario, pues, dadas las conversiones que se realizan en ciertas transacciones de
caracteres en los archivos abiertos en modo texto, se producirían errores en el
posicionamiento en el fichero al usar dicha función. Veamos un ejemplo de uso de
fseek():
#include <stdio.h>
int LeeCaracter(FILE *fp,long pos,int origen)
{
if (fseek(fp,pos,origen))
return(EOF);
return(getc(fp));
}
Antes de terminar este tema, es necesario comentar la existencia de tres
ficheros que son abiertos de forma automática al comenzar la ejecución del programa,
y cerrados, también de forma automática, al terminar la misma. Estos archivos son la
entrada standard (stdin), la salida standard (stdout) y la salida standard de error
(stderr). Normalmente estas ficheros están asociados a la consola, pero pueden
redireccionarse a cualquier otro dispositivo. Además, dado que son exactamente igual
que ficheros, pueden usarse sus nombres en los mismos lugares que se usan las
variables de tipo FILE *, por lo cual, cualquier función de fichero puede usarse con la
consola usando estos archivos standard abiertos al comenzar el programa. Es por ello,
que podemos leer, por ejemplo, una cadena desde el teclado de la siguiente forma:
char cadena[100];
fgets(cadena,100,stdin);
Y escribir dicha cadena, por ejemplo en la salida standard de error, de la forma:
fputs(cadena,stderr);