Universidad Carlos III de Madrid

Grado en Ing. Telemática/Sist. Audiovisuales/Sist. de Comunicaciones

Arquitectura de Sistemas

Septiembre 2012 - Enero 2013

3. Lectura y escritura de ficheros

En C puedes realizar las operaciones de lectura y escritura de varias maneras:

  • Leer y escribir carácter a carácter (o lo que es lo mismo, byte a byte), con funciones como fgetc y fputc.

  • Leer y escribir línea a línea, con funciones como fgets y fputs.

  • Leer y escribir un bloque de caracteres (o bytes) cada vez, con fread y fwrite.

Para este curso nos vamos a centrar en la última, la lectura por bloques, que nos será útil tanto para ficheros de texto como para binarios.

3.1. Lectura/Escritura por bloques

En C contamos con las funciones fread y fwrite para realizar operaciones E/S por bloques.

La sintaxis de fread es la siguiente:

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t n, FILE *stream);

Aquí ptr es un array donde se van a almacenar los datos que se van a leer. size indica el tamaño de cada elemento del array. n especifica el número de elementos que se van a leer. stream es un puntero a un fichero que ha tenido que haber sido abierto previamente. size_t es un tipo que está definido también en la cabecera stdio.h. La función devuelve el número de elementos que realmente se han leído (que puede ser menor que n); si todo ha ido bien o aún no se ha llegado al final del fichero, ese número devuelto debería ser mayor que 0 e igual o menor (en caso de que no sepamos la longitud de lo que vamos a leer) que el tercer argumento, n.

Si fread no devuelve el resultado esperado, habrá que distinguir si es porque llegamos al final del fichero o por un error; para ello contamos con las funciones feof y ferror para saber exactamente qué pasó, que veremos a continuación.

La sintaxis de fwrite es la siguiente:

#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream);

ptr hace referencia a un array con los datos que se van a escribir al fichero abierto apuntado por stream. size indica el tamaño de cada elemento del array. n especifica el número de elementos que se van a escribir. stream es un puntero a un fichero que ha tenido que haber sido abierto previamente. La función devuelve el número de elementos que se han escrito; luego si no ha habido ningún error, ese número devuelto debería ser igual que el tercer argumento, n. El valor devuelto puede ser menos que este n si ha habido algún error.

Como se ha comentado anteriormente, tenemos la función feof, que se usa para saber cuándo hemos llegado al final del fichero:

#include <stdio.h>
int feof(FILE *stream);

Esta función devuelve 0 si aún no se ha llegado al final del fichero. Si se ha llegado, devuelve un entero distinto de cero.

Finalmente, contamos con la función ferror para saber si ha ocurrido algún error en la lectura o escritura, que devolverá 0 si no ha ocurrido ninguno y un valor distinto de cero en caso contrario:

#include <stdio.h>
int ferror(FILE *stream);

El siguiente programa escribe en bloque un array de números y luego los lee de uno en uno para calcular su suma.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdio.h>
#define TAM 6

int main(void)
{
  FILE *fichero;
  int numeros[TAM] = {20, 20, 20, 20, 20, 20};
  char nombre_fichero[] = "numeros.bin";
  //Esta variable irá recogiendo los resultados de lectura/escritura
  size_t resultado;

  /* Abrimos para escritura en bloque*/
  fichero = fopen(nombre_fichero, "w");
  if (fichero == NULL)
  {
	  printf("El fichero no se ha podido abrir para escritura.\n");
	  return -1;
  }
  // Se escribe en bloque los elementos del vector.
  resultado = fwrite(numeros, sizeof(int), TAM, fichero);
  if (resultado!=TAM)
  {
	  printf("No se han escrito todos los %d números del array.\n", TAM);
  }

  if (fclose(fichero)!=0)
  {
    printf("No se ha podido cerrar el fichero.\n");
	return -1;
  }

  /* Abrimos para lectura */
  int suma = 0;
  fichero = fopen(nombre_fichero, "r");
  if (fichero == NULL)
  {
	  printf("El fichero no se ha podido abrir para lectura.\n");
	  return -1;
  }
  // Se lee número por número.
  int num;
  while (!feof(fichero))
  {
    resultado = fread(&num, sizeof(int), 1, fichero);
	if (resultado != 1)
	{
		break;
	}
	suma = suma + num;
  }

  if (ferror(fichero)!=0)
  {
	  printf("Ha ocurrido algún error en la lectura de números.\n");
  }
  else
  {
	  printf("La suma de los números leídos es: %d\n.",suma);
  }

  if (fclose(fichero)!=0)
  {
    printf("No se ha podido cerrar el fichero.\n");
	return -1;
  }
  return 0;
}
 

A la hora de escribir el archivo, conocemos de antemano el tamaño del array y se puede escribir todo el bloque. En caso de que hubiera que escribir una serie de números, sin saber exactamente cuántos, se tendría que ir escribiendo uno por uno.

La sentencia break de la línea 47 se usa para salir de la lectura, pues ha habido un error o final de fichero y no se necesita efectuar ninguna operación más dentro del bucle. Al salir, se comprueba qué es lo que pasó realmente, para advertir al usuario.