pub:c_io

    • Salida con formato: printf()
    • Entrada de datos con formato: scanf()
    • Entrada de caracteres: getchar()
    • Salida de caracteres: putchar()
    • Entrada y salida de cadenas
    • Apertura y cierre de un fichero
      • Fin de fichero
    • Entrada y salida de texto
      • Entrada y salida de caracteres
      • Entrada y salida de cadenas
    • Lectura y escritura de datos binarios
    • Entrada y salida con formato
    • Entrada y salida mediante acceso directo
    • Otras operaciones sobre ficheros

E/S estándar

Las entradas y salidas de información de un programa pueden realizarse:

  • Sobre las unidades de almacenamiento
    • Apertura del archivo
    • Operación de lectura/escritura sobre el archivo
    • Cierre del archivo
  • Sobre dispositivos periféricos
    • Directamente sobre los periféricos: operación hardware
    • A través de los controladores de los periféricos
    • Con los recursos ofrecidos por el sistema operativo

Cuando un programa está en ejecución, se convierte en un proceso que dispone de:

  • Un canal estándar de entrada asociado por defecto al teclado: sdtin
    • Lo que el usuario escribe en el teclado va a parar al canal stdin
  • Un canal estándar de salida asociado por defecto a la pantalla: stdout
    • Lo que el proceso escribe en el canal stdout va a parar a la pantalla
  • Un canal estándar para salida de errores asociado por defecto a la pantalla: stderr
    • Lo que el proceso escribe en el canal stderr también va a parar a la pantalla

El estándar ANSI C creó un conjunto de funciones estándar para la entrada y salida a través de los ficheros estándar stdin y stdout

  • Están definidas en el archivo stdio.h, por lo que para utilizarlas en un programa es preciso incluir la línea:
   #include <stdio.h>

Las más importantes son:

  • printf() para escribir datos con formato
  • scanf() para leer datos con formato
  • getchar() para leer caracteres del teclado
  • putchar() para escribir caracteres en pantalla
Salida con formato: printf()

La función devuelve el número de bytes escritos o EOF en caso de error

  • Plantilla de utilización:
   printf("cadena_de_control", lista_de_argumentos);
  • Elementos en la “cadena_de_control” :
    • Caracteres y símbolos ASCII normales
    • Caracteres de escape o secuencias de barra invertida
      • Comienzan siempre con símbolo «»
    • Especificadores de formato para la representación de los valores almacenados en variables
      • Comienzan siempre con el símbolo «%»
  • La “lista_de_argumentos” es una relación de las variables (separadas por comas) cuyos contenidos se quiere mostrar
    • Tiene que haber al menos tantas variables como especificadores de formato
  • Caracteres de escape o secuencias de barra invertida más habituales:
\a Alarma (pitido)\'Comilla simple
\b Espacio atrás \“ Comillas dobles
\f Salto de página\\ Barra invertida
\n Nueva línea \oo Carácter ASCII en octal
\r Retorno de línea\xHH Carácter ASCII en hexadecimal
\t Tabulador\0 Carácter nulo (Código ASCII cero)
  • Sintaxis de los especificadores de formato:
   %[flags][anchura][.precision][prefijo-tipo] formato
  • flags: Opcional:
    • - justifica a la izquierda
    • + fuerza la aparición del signo siempre
    • 0 completa con ceros a la izquierda todo el campo
  • anchura: Opcional: Ancho del campo en el que aparecerá el dato
  • .precision: Opcional:
    • En enteros, número de dígitos
    • En reales, número de dígitos decimales
    • En cadenas, número de caracteres
  • prefijo-tipo: Opcional, para indicar a la función como debe interpretar el dato contenido en la memoria:
    • h interpreta un short
    • l interpreta long en los enteros o double en los reales
    • L interpreta un long double
  • formato: Obligatorio, para determinar el tipo de dato de la variable cuyo contenido se va a mostrar:
    • d Entero con signo en mostrado en decimal
    • u Entero sin signo mostrado en decimal
    • o Entero sin signo mostrado en octal
    • x Entero sin signo mostrado en hexadecimal
    • f Número real en formato [–]ddd.ddd
    • e Número real en formato [–]d.dddE[±]ddd
    • g Número real en el formato más corto
    • c Carácter
    • s Cadena de caracteres
  • Ejemplos
   printf("Número entero: %d", edad);
   printf("Letra:%c t Octal:%o t Hexadecimal: %x",codigo, codigo, codigo);
   printf("Número real: %f t %E t %G", estatura, estatura, estatura);
Entrada con formato: scanf()

La función devuelve el número de datos leídos correctamente o EOF en caso de error

  • Plantilla de utilización:
   scanf("cadena_de_control", lista_de_argumentos);
  • Elementos de la cadena_de_control :
    • No es una cadena que aparezca en pantalla
    • Se corresponde con la cadena que la función espera encontrarse en el teclado carácter a carácter
    • Incluye los caracteres de separación o espacios en blanco que separan los especificadores de formato. Pueden ser
      • El espacio en blanco “ ”
      • El tabulador t
      • El retorno de carro n
    • Incluye los especificadores de formato que provocan la captura de un dato
  • Sintaxis de los especificadores de formato
   %[*][anchura][prefijo-tipo] formato
  • [*]: Opcional: anula la asignación al siguiente dato
   scanf("%d %*s”, &valor); /* Lee el dato y la cadena que se teclee a continuación del valor entero,
                               pero no se asigna a ninguna variable */
  • anchura: Opcional: Número de caracteres a leer (se ignoran los restantes)
  • prefijo-tipo: Opcional:
    • En enteros, número de dígitos
    • En reales, número de dígitos decimales
    • En cadenas, número de caracteres
  • formato: Obligatorio, determina el tipo de dato (igual que en printf())
  • La lista de argumentos es una relación de las direcciones de memoria de las variables (separadas por comas)
    • La dirección de una variable se obtiene precediendo el nombre de la variable del operador «&»
    • Debe haber el mismo número de variables que especificadores de formato

El buffer de teclado es una zona de almacenamiento intermedio asociada a la entrada estándar stdin

  • Se almacenan los códigos ASCII de las teclas pulsadas
  • Se valida cuando se pulsa la tecla INTRO
  • Sólo se eliminan del buffer los códigos de los caracteres leídos
  • Es la zona de memoria donde scanf() obtiene los valores
  • Puede que no se cojan todos los caracteres tecleados
  • Se quedan hasta la siguiente lectura del buffer del teclado
  • Hay una función definida en stdio.h que borra este buffer
   fflush(sdtin);
Entrada de caracteres: getchar()

Función para leer caracteres del teclado. Prototipo:

   int getchar(void);
  • Definida en stdio.h
  • No requiere ningún argumento
  • Devuelve el código ASCII de la tecla pulsada o EOF en caso de error
  • Solo finaliza, cogiendo un código, cuando se pulsa la tecla INTRO
    • Si se ha pulsado más de una tecla, quedan en el buffer del teclado
  • Ejemplo:
   char a;
   a = getchar(void);
   scanf("%c", &a);
  • * Las dos sentencias son equivalentes
  • Las teclas especiales (F1…F12) y las combinaciones con CTRL y ALT generan dos códigos de 8 bits, es decir, equivalen a dos pulsaciones (dos caracteres)
Salida de caracteres: putchar()

Función para mostrar caracteres en la pantalla. Prototipo:

   int putchar(int variable);
  • Definida en stdio.h
  • Como argumento requiere el nombre de la variable que contiene el código a mostrar
  • Devuelve en un entero el código ASCII del carácter mostrado o EOF en caso de error
  • Ejemplo:
   char a;
   putchar((int)a);
   printf("%c", a);
  • * Las dos sentencias son equivalentes

Lectura y escritura de cadenas

En lenguaje C no existe un tipo de dato de cadena de caracteres, los arrays de caracteres o cadenas de caracteres son vectores:

  • Cuyos elementos se almacenan consecutivamente en memoria
  • Se puede hacer referencia a todo el conjunto mediante un único identificador
  • Para referenciar a un elemento se utiliza el identificador y un índice entre corchetes
   cadena[indice]
  • El último elemento es siempre el carácter nulo 0
  • Declaración de un array (incluido el nulo final):
   char nombrevariable[NUMERO_DE_ELEMENTOS];
  • Se tratarán con detalle más adelante
Lectura de cadenas: gets( )
   gets(char *cadena);
  • Definida en stdio.h
  • Necesita, como argumento, el identificador de una cadena de caracteres
  • Lee todos los caracteres tecleados hasta el INTRO que lo recoge y lo sustituye por '0' en memoria
  • Ejemplo:
   #define NUM_ELEM 100
   char cadena[NUM_ELEM];  /* Declaración */
   gets(cadena);          /* Lee una cadena */
   scanf("%s", cadena);    /* Lee una cadena */
  • Las dos sentencias son equivalentes
  • Leen todo lo tecleado (sea cual sea el valor de NUM_ELEM)
Escritura de cadenas: puts( )
   puts(cadena);
  • Definida en stdio.h
  • Necesita, como argumento, el identificador de una cadena de caracteres o la propia cadena entre comillas dobles
  • Lleva a pantalla los símbolos ASCII de los códigos almacenados hasta encontrar el nulo '0' que es sustituido por un INTRO
  • Ejemplo:
   puts(cadena)           /* Muestra una cadena */
   printf("%s", cadena);   /* Muestra una cadena */
  • Las dos sentencias son equivalentes
  • La función printf() puede utilizarse para mostrar en pantalla la cadena que va entre comillas como argumento

Ficheros y secuencias

El lenguaje C proporciona mecanismos para la entrada y salida por ficheros:

  • Que no dependen del dispositivo físico sobre el que actúen
  • Son funciones genéricas, potentes y flexibles
  • Distingue dos tipos de elementos
    • Ficheros
    • Secuencias o streams
  • fichero: Dispositivo físico y real sobre el que se realizan las operaciones de entrada o salida
  • secuencia (stream): Ente abstracto sobre el que se realiza una operación de entrada o salida genérica
    • Las secuencias, en muchas ocasiones, se asocian a dispositivos físicos o a ficheros:
      • La secuencia de salida estándar se asocia a la pantalla
      • La secuencia de entrada estándar se asocia al teclado
  • El concepto de fichero puede aplicarse, además de a ficheros en disco, a impresoras, terminales, controladores, etc.
  • El concepto de secuencia puede entenderse como un buffer de datos o un dispositivo lógico que proporciona un camino para los datos en las operaciones de entrada o salida
  • Cuando se abre un fichero para operaciones de entrada y/o salida, se le asocia una secuencia, un camino que permitirá la transferencia de información entre ese fichero y el programa.
  • El lenguaje C contempla los ficheros como secuencias continuas de bytes que pueden ser accedidos de forma individual:
    • Todas las funciones de E/S sirven para todas las secuencias, sólo hay que redirigirlas al fichero deseado

  • El sistema operativo (DOS/Windows) admite que un fichero pueda ser abierto de dos modos:
    • En modo texto: Los bytes de la secuencia son códigos ASCII
    • En modo binario: Los bytes de la secuencia son códigos binarios de 8 bits
  • En los sistemas Unix y Linux no existe tal distinción
    • No hay ninguna indicación añadida al modo de apertura de un fichero

En modo texto el final de la línea se representa:

  • En lenguaje C: como «n» (En ASCII CR: Retorno de carro, código 10)
  • En el sistema operativo Windows: como la sucesión de los códigos ASCII: CR+LR (Retorno de carro + Salto de línea, códigos 10 y 13)
  • Las funciones de lectura en lenguaje C al encontrar la secuencia CR+LF, la convierten a «n»
  • Las funciones de lectura al encontrar el código correspondiente a Ctrl+D (Ctrl+Z en Windows) lo interpretan como EOF (End Of File\, fin de fichero).
  • Ejemplo: Si en un fichero de texto del DOS (*.TXT) escribimos:
   En un lugar de la mancha,
   de cuyo nombre no quiero acordarme.
  • Al leer el fichero en modo binario desde un programa en lenguaje C se obtendrá la siguiente secencia:
   En un lugar de la mancha, r\nde cuyo nombre no quiero acordarme.EOF
  • Si se abre en modo texto , lo que se recogerá será lo siguiente:
   En un lugar de la mancha, nde cuyo nombre no quiero acordarme.EOF

Al ejecutar un programa en modo consola, automáticamente el sistema operativo abre tres secuencias estándar:

  • stdin
    • Entrada estándar. Se asocia al teclado
  • stdout
    • Salida estándar. Se asocia a la pantalla
  • stderr
    • Salida estándar de errores. Se asocia a la pantalla
  • Las secuencias estándar tienen asociado un buffer de memoria física cada una
  • Las E/S por consola operan igual que las E/S por ficheros, pero redirigen la operación real sobre las secuencias estándar stdin, stdout y stderr
Apertura y cierre de un fichero

Para acceder a los ficheros se precisa un descriptor del fichero (puntero de tipo FILE)

  • Declaración:
   FILE *puntero;
  • FILE es una constante definida en stdio.h
  • El descriptor apunta a un buffer que contiene toda la información necesaria para operar con el fichero
  • Se utiliza para hacer referencia al fichero en todas las operaciones sobre este
  • Debe declararse antes de utilizarse
  • El descriptor se inicializa al abrirsin error un fichero al que se le hace apuntar

Antes de cualquier operación sobre un fichero es preciso abrir el fichero, utilizando la función fopen()

  • Declaración:
   FILE * fopen(char *nombrearchivo, char *modo);
  • Devuelve:
    • Un descriptor de tipo FILE que apunta al fichero abierto
    • Un descriptor nulo (NULL) en caso de error
  • Recibe dos cadenas de caracteres:
    • La primera con el nombre del fichero (con la ruta de acceso)
    • La segunda con el modo de apertura que se selecciona
Modos de apertura de ficheros
Modo Texto Binario Observaciones
Abrir para leer"r" "rb" Si no existe, se produce error
Crear para escribir"w" "wb" Si existe, se pierde el contenido
Abrir o crear para añadir"a" "ab" Si no existe, se crea
Abrir para leer y/o escribir"r+" "rb+" Debe existir
Crear para leer y/o escribir"w+" "wb+" Si existe, se pierde el contenido
Abrir o crear para añadir y/o leer"a+" "ab+" Si no existe, se crea

Mientras el fichero esté abierto, el descriptor correspondiente apunta a una estructura que contiene toda la información necesaria sobre el fichero:

  • Nombre, tamaño, atributos, posición del apuntador para lecturas y/o escritura, etc.

Al finalizar un programa que utiliza ficheros:

  • Si termina normalmente, los ficheros que estuviesen abiertos son cerrados por el sistema operativo.
  • Un fichero no cerrado correctamente queda inutilizado y la información que pudiera contener se pierde y queda inaccesible.

En previsión de finalizaciones anómalas, tras las operaciones de lectura y/o escritura, es preciso cerrar los ficheros, utilizando la función fclose():

  • Declaración
   int fclose(FILE *descriptor);
  • Devuelve
    • un entero de valor cero si el cierre ha sido correcto
    • EOF en caso de error
  • Recibe como argumento el descriptor al fichero que se quiere cerrar
  • Ejemplo de apertura y cierre de un fichero:
   FILE *pf;   /* descriptor a fichero */
   if ((pf=fopen("misdatos/prueba.x","w+")) == NULL) {
      puts("nNo es posible crear el fichero");
      exit(0);
   } else printf("nEl fichero se ha abierto");
                  /* Tras realizar todas las operaciones necesarias,
                es preciso cerrar el fichero antes de finalizar
                     el programa */
   fclose(pf);    /* El fichero ha quedado cerrado */

El carácter de fin de fichero está representado por la constante simbólica EOF, definida en stdio.h

  • Es el último byte del fichero
  • Cuando se leen bytes del fichero (con fgetc()) puede leerse el EOF y no distinguirse como último carácter, especialmente cuando se trata de secuencias binarias
  • La función feof(), declarada en stdio.h devuelve un valor distinto de cero (verdadero/true) cuando se lee el byte de fin de fichero (EOF)
   while (!feof(puntfile))
   {
      /* Operaciones sobre el fichero abierto */
   }
Entrada y salida de texto

Las funciones de lectura y escritura de caracteres en ficheros están definidas en stdio.h

   int fgetc( FILE *pf);
  • Lee un carácter en el fichero cuyo descriptor recibe
  • Devuelve el carácter leído en un entero o EOF en caso de error
   int fputc( int car, FILE *pf);
  • Escribe un carácter en el fichero cuyo descriptor recibe
  • Devuelve EOF en caso de error
  • Recibe como argumentos:
    • car: El carácter a escribir
    • pf: El descriptor al fichero
  • Ejemplos de lectura y escritura de un carácter en un fichero:
   FILE *pf1, *pf2;
   char letra;
   pf1 = fopen("leer.txt", "r");
   letra = fgetc(pf1);     /* Lee un carácter */
   pf2 = fopen ("escribir.txt", "w");
   fputc(letra, pf2);      /* Escribe un carácter */
   fclose(pf1);
   fclose(pf2);

Las funciones de lectura y escritura de cadenas de texto en ficheros están definidas en stdio.h

   char * fgets(char *cad, int numcar, FILE *pf);
  • Devuelve un puntero a la cadena leída o un puntero nulo en caso de error
  • Recibe como argumentos
    • cad: Un puntero a la zona de memoria donde se almacenará la cadena
    • numcar-1: Es el número de caracteres a leer. Será añadido el carácter nulo
    • pf: El puntero al fichero
  • Si encuentra un carácter de fin de línea (n), éste será el último carácter leído
   int fputs(char *puntcad, FILE *pf);
  • Devuelve en un entero el último carácter escrito o EOF en caso de error
  • Recibe como argumentos
    • puntcad: Un puntero a cadena que se quiere escribir
    • pf: El puntero al fichero
  • Ejemplos de lectura y escritura de una cadena de caracteres en un fichero:
   FILE *pf1, *pf2;
   char lect[50];
   char escr[]="Mensaje a guardar en el fichero";
   int num=50-1;
   pf1 = fopen("leer.txt", "r");
   fgets(lect, num, pf1);
      /* Lee una cadena de 49 caracteres de leer.txt */
   pf2 = fopen ("escribir.txt", "w");
   fputs(escr, pf2);
      /* Escribe la cadena "Mensaje a guardar en el fichero escribir.txt */
   fclose(pf1);
   fclose(pf2);
Lectura y escritura de datos binarios

Para la lectura de un conjunto de datos:

   unsigned fread(void *buf, unsigned numbytes, unsigned numdat, FILE *pf);

Para la escritura de un conjunto de datos:

   unsigned fwrite(void *buf, unsigned numbytes, unsigned numdat, FILE *pf);
  • Devuelven el número de datos leídos o escritos
  • Reciben
    • buf: Un puntero a los datos que son leídos o escritos
    • numbytes: representa el número de bytes de que consta cada uno de los datos a a leer o escribir (se obtiene con sizeof())
    • numdat: representa el número total de datos, items o elementos a leer o escribir
    • pf: es el descriptor al fichero sobre el que van a leer o escribir
  • Mediante el manejo de datos binarios, el contenido de los ficheros puede ser similar al contenido de las variables en memoria
  • Es importante cuando se trabaja con estructuras y uniones
  • Las funciones fread y fwrite pertenecen al ANSI C y están definidas en stdio.h
  • Ejemplo de lectura y escritura de datos binarios:
   FILE *pf;
   float valor1=3.5, valor2;
   pf=fopen ("archivo.dat", "ab+");       /* "a+" en Unix */
   fwrite(&valor1, sizeof(valor), 1, pf); /* Escribe */
   fread(&valor2, sizeof(float), 1, pf);  /* Lee */
Entrada y salida por ficheros con formato
  • Las funciones fprintf() y fscanf() son idénticas a printf() y scanf() respectivamente, pero operan sobre un descriptor. También están declaradas en stdio.h
   int fprintf(FILE *pf, const char* cad_control, lista_argumentos);
   int fscanf(FILE *pf, const char* cad_control, lista_argumentos);
  • Argumentos que reciben:
    • pf: Representa un puntero al fichero sobre el que opera la función
    • cad_control: Es la cadena en la que s e incluyen los especificadores de formato y sus modificadores
    • lista_argumentos: Representa la lista de argumentos que se corresponden con los especificadores de formato de la cadena de control (lista de variables cuyos contenidos quieren escribirse o leerse sobre el fichero)
  • Devuelven:
    • fprintf() devuelve en un entero el número de bytes escritos
    • fscanf() devuelve en un entero el número de campos correctamente escaneados y almacenados o EOF en caso de error.
  • La llamada:
   fprintf(stdout, "Número: %d", num);
  • es equivalente a la llamada:
   printf("Número: %d", num);
  • Cuando se escribe en un fichero mediante la función fprintf(pf, …) el contenido del fichero es similar al mostrado en pantalla con la función fprintf(stdout, …)
  • Ejemplo de escritura y lectura con formato:
   FILE *pf;
   int i = 100;
   char c = 'C';
   float f = 1.234;
   pf = fopen("prueba.dat", "w+");
   fprintf(pf, "%d %c %f", i, c, f);   /* Escribe en el fichero */
   fscanf(pf, "%d %c %f", &i, &c, &f); /* Lee los mismos datos en el fichero */
   fclose(pf);
Entrada y salida mediante acceso directo

El acceso a los ficheros puede hacerse:

  • En modo secuencial: los datos son leídos o escritos seguidos, sin saltos
  • En modo aleatorio: es posible acceder a cualquier posición para realizar una operación de lectura o de escritura

Los punteros de tipo FILE apuntan a una estructura creada por el sistema operativo que controla las operaciones sobre ese fichero.

  • La estructura incluye un puntero de lectura-escritura que determina la posición actual en la que se va a escribir o a leer en cada momento
  • Al abrir un fichero, el puntero de lectura-escritura apunta al comienzo del fichero (salvo se si abre para añadir)

La función fseek(), definida en stdio.h, permite el movimiento aleatorio por el fichero abierto estableciendo una nueva posición para el apuntador de lectura-escritura:

   int fseek(FILE *pf, long nbytes, int origen);
  • Devuelve un valor verdadero si el movimiento se realizó con éxito o cero en caso contrario
  • Recibe
    • pf: Descriptor con el que se opera
    • nbytes: Número de bytes que queremos desplazar el apuntador de lectura-escritura del fichero con relación al punto de partida
    • origen: Representa el punto que se toma como origen o referencia. Se utilizan constantes simbólicas definidas en stdio.h:
      • SEEK_SET corresponde al principio del fichero
      • SEEK_CUR corresponde a la posición actual
      • SEEK_END corresponde al final del fichero
  • Ejemplo:
   #define N 5                   /* Será la fila 5 */
   int matriz[20][4], *punt;     /* 20 filas y 4 columnas*/
   FILE *pf;
   punt = matriz;
 
      /* Apertura del fichero sin errores */
   fwrite(punt, sizeof(int), 20*4, pf);
      /* Escribe la matriz en un fichero */
   fseek(pf, sizeof(int)*4*N, SEEK_SET);
      /* Apunta a los datos de la fila N, al ser
         4*sizeof(int) el tamaño de una fila */
   fread(&matriz[N][0], sizeof(int), 4, pf);
      /* Lee los datos de la fila N */
Otras operaciones sobre ficheros

  • Función ftell()
long ftell(FILE *pf);
  • * Devuelve un entero largo con la posición del apuntador de lectura-esritura del fichero con respecto al principio del fichero.
    • Recibe el puntero a un fichero abierto
    • Está definida en stdio.h

  • Función rewind()
void rewind(FILE *pf);
  • * Inicializa el indicador de posición o apuntador de lectura-escritura haciendo que apunte al principio del fichero
    • No devuelve nada
    • Recibe el puntero a un fichero abierto
    • Está definida en stdio.h

  • Función remove()
int remove(char *nombre_archivo);
  • * Borra el fichero cuyo nombre se especifique en la cadena que recibe como argumento
    • Devuelve cero si la operación tuvo éxito y -1 si se produce algún error
    • En caso de error, la variable global errno, definida en errno.h indicará el tipo de error
    • Está definida en stdio.h

  • Función fflush()
int fflush(FILE *pf);
  • * Vacía los bufferes de entrada/salida asociados al flujo descrito por pf
    • Devuelve cero si la operación tuvo éxito y NULL si se produce algún error
    • Está definida en stdio.h
    • Es muy utilizada para borrar el buffer del teclado y cuando se trabaja con impresoras

  • Función tmpfile()
FILE * tmpfile(void));
  • * Crea un fichero temporal que es borrado automáticamente cuando el fichero es cerrado o al terminar el programa
    • El fichero temporal se crea en modo “w+”
    • Devuelve un descriptor al fichero temporal creado o un puntero nulo si no se puede crear
    • Está definida en stdio.h


  • pub/c_io.txt
  • Última modificación: 2020/09/28 11:44
  • (editor externo)