Leer un fichero en C

Ahora que ya sabemos abrir o crear un fichero y escribir en él voy a explicar como tenemos que hacer para leer su contenido. El mecanismo de lectura es sencillo, cuando abrimos un fichero el puntero se encuentra al inicio de este. Si hacemos una operación de lectura se lee el elemento sobre el que está el puntero, y a continuación el puntero avanza hacia el siguiente elemento, pero hasta que no se haga otra operación de lectura no se lee. A continuación detallo cuales son las principales operaciones o funciones de lectura de ficheros.

fgetc

La función fgetc se utiliza para leer un carácter de un fichero de texto, y devuelve dicho carácter. La sintaxis general es:

fgetc(puntero);

El parámetro puntero es el identificador de la variable puntero a FILE correspondiente al fichero de texto que estamos leyendo. Veamos un ejemplo:

#include <stdio.h>


int main(void) {

	/* Declaramos la variable fichero como puntero a FILE. */
	FILE *fichero;

	/* Declaramos la variable caracter de tipo char. */
	char caracter;

	/* Abrimos "fichero1.txt" en modo texto y
	 * guardamos su direccion en el puntero. */
	fichero = fopen("fichero1.txt", "rt");

	if (fichero == NULL) {
		printf("Error: No se ha podido crear el fichero fichero1.txt");
	} else {
		/* Se obtiene el primer caracter del fichero
		 * y se almacena en la variable caracter. */
		caracter = fgetc(fichero);

		printf("El primer caracter leído es: \"%c\"\n", caracter);

		/* Cerramos "fichero1.txt". */
		fclose(fichero);
	}
}

Como podemos ver, tras leer el primer carácter del archivo "fichero1.txt" este se almacena en la variable caracter, y a continuación imprimimos un mensaje indicando cuál es ese primer caracter.

feof

La función feof nos permite detectar el final de un fichero (end-of-file) y así poder averiguar todos los caracteres que existen dentro del mismo. La sintaxis general es:

feof(puntero);

Donde puntero es el identificador de la variable puntero al fichero. Esta llamada a la función feof nos devolverá un si no se ha llegado al final del fichero, y por el contrario devolverá un valor distinto de si se llega al final. Veámoslo con un ejemplo:

#include <stdio.h>

int main(void) {

	/* Declaramos la variable fichero como puntero a FILE. */
	FILE *fichero;

	/* Declaramos la variable caracter de tipo char. */
	char caracter;

	/* Abrimos "fichero1.txt" en modo texto y
	 * guardamos su direccion en el puntero. */
	fichero = fopen("fichero1.txt", "rt");

	if (fichero == NULL) {
		printf("Error: No se ha podido crear el fichero fichero1.txt");
	} else {
		/* Se obtiene el primer caracter del fichero
		 * y se almacena en la variable caracter. */
		caracter = fgetc(fichero);

		while (feof(fichero) == 0) {
			printf("%c\n", caracter);
			caracter = fgetc(fichero);
		}

		/* Cerramos "fichero1.txt". */
		fclose(fichero);
	}
}

Este ejemplo lo que hace es leer un carácter, a continuación comprueba si hemos llegado al final del fichero, y si no se ha llegado al final lo imprime. Este proceso se repite por cada uno de los caracteres escritos en el fichero (incluidos saltos de línea).

fgets

Con la función fgets podemos leer una cadena de caracteres, su sintaxis general es:

fgets(cadena, tamaño, puntero);

Donde cadena es la variable que va a almacenar la cadena de caracteres que se lea del fichero, tamaño es la cantidad máxima de caracteres que vamos a leer del fichero (y almacenar en cadena), y puntero es el identificador de la variable puntero al fichero. Decimos que tamaño es la cantidad máxima de caracteres que vamos a leer del fichero por que la lectura se podría detener antes si encuentra un salto de línea. La función fgets devuelve la cadena de caracteres leída o NULL si hubiera un error o se llegase al final del archivo. El mismo ejemplo de antes utilizando la función fgets sería así:

#include <stdio.h>

int main(void) {

	/* Declaramos la variable fichero como puntero a FILE. */
	FILE *fichero;

	/* Declaramos la variable cadena de tipo array char. */
	char cadena[256];

	/* Declaramos la variable reslutado como puntero. */
	char *resultado;

	/* Abrimos "fichero1.txt" en modo texto y
	 * guardamos su direccion en el puntero. */
	fichero = fopen("fichero1.txt", "rt");

	if (fichero == NULL) {
		printf("Error: No se ha podido crear el fichero fichero1.txt");
	} else {
		/* Se obtiene la cadena de caracteres de
		 * tamaño 256 dentro de fichero1.txt. */
		resultado = fgets(cadena, 256, fichero);

		while (resultado != NULL) {
			printf("%s", cadena);
			resultado = fgets(cadena, 256, fichero);
		}

		/* Cerramos "fichero1.txt". */
		fclose(fichero);
	}
}

fread

La función fread ofrece la misma potencia que la función fread, pero para leer, y nos permite leer datos de diferentes tipos. La sintaxis es la misma:

fread(direccion_dato, tamaño, veces, puntero);

Donde direccion_dato es la dirección de la variable o elemento donde queremos almacenar los datos leídos del fichero indicado por la variable puntero. En tamaño debemos indicar el tamaño en bytes que queremos leer del fichero, y en veces indicamos cuántos elementos de tamaño tamaño vamos a leer. Se nos podría plantear la duda de qué valores indicamos en tamaño y veces, pero, como lo normal es que el fichero que vayamos a leer también lo hayamos creado nosotros, conoceremos estos valores, ya que los hemos usado previamente con la función frwrite.

Anteriormente hemos creado un fichero binario, llamado empleados.dat, en el que hemos almacenado estructuras con  la siguiente definición:

struct datos_empleado {
	char nombre[30];
	int edad;
};

Si queremos leer ese fichero binario y  mostrar todas las estructuras almacenadas en él podríamos hacerlo con un programa como este de ejemplo:

#include <stdio.h>

struct datos_empleado {
	char nombre[30];
	int edad;
};

int main(void) {

	/* Declaramos la variable fichero como puntero a FILE. */
	FILE *fichero;

	struct datos_empleado empleado;

	/* Abrimos "empleados.dat" en modo texto y
	 * guardamos su direccion en el puntero. */
	fichero = fopen("empleados.dat", "rb");

	if (fichero == NULL) {
		printf("Error: No se ha podido crear el fichero empleados.dat");
	} else {
		/* Leemos la primera estructura empleado
		 * del fichero "empleados.dat". */
		fread(&empleado, sizeof(empleado), 1, fichero);

		while (feof(fichero) == 0) {
			printf("\n\nNombre: %s", empleado.nombre);
			printf("\nEdad: %d", empleado.edad);

			/* Si aun no hemos llegado al final del fichero
			 * "empleados.dat" seguimos leyendo estructuras. */
			fread(&empleado, sizeof(empleado), 1, fichero);
		}

		/* Cerramos "empleados.dat". */
		fclose(fichero);
	}
}

De este modo, podremos comprobar que imprime cada una de las estructuras hasta llegar al final del fichero. Bien, ¿y si lo que queremos es que el programa imprima solo una estructura que cumpla un requisito? Por ejemplo, solo los datos de un empleado que tenga una edad determinada. En ese caso podemos hacer un programa que solicite al usuario introducir una edad y pedirle al programa que solo imprima los nombres de los empleados que cumplan con esa condición. Este sería un ejemplo:

#include <stdio.h>

struct datos_empleado {
	char nombre[30];
	int edad;
};

int main(void) {

	/* Declaramos la variable fichero como puntero a FILE. */
	FILE *fichero;

	struct datos_empleado empleado;
	int filtroEdad;

	/* Creamos un flag con valor inicial 0, solo
	 * cambiara su valor a 1 cuando se encuentre
	 * un empleado con la edad buscada. */
	int encontrado = 0;

	/* Pedimos al usuario una edad a buscar. */
	printf("Edad del empleado: ");
	scanf("%d", &filtroEdad);

	/* Abrimos "empleados.dat" en modo texto y
	 * guardamos su direccion en el puntero. */
	fichero = fopen("empleados.dat", "rb");

	if (fichero == NULL) {
		printf("Error: No se ha podido crear el fichero empleados.dat");
	} else {
		/* Leemos la primera estructura empleado
		 * del fichero "empleados.dat". */
		fread(&empleado, sizeof(empleado), 1, fichero);

		while (feof(fichero) == 0) {

			if(empleado.edad == filtroEdad){
				printf("\n\nNombre: %s", empleado.nombre);
				printf("\nEdad: %d", empleado.edad);

				/* Activamos el flag a true (1), para indicar
				 * que hemos encontrado al algo. */
				encontrado = 1;
			}

			/* Si aun no hemos llegado al final del fichero
			 * "empleados.dat" seguimos leyendo estructuras. */
			fread(&empleado, sizeof(empleado), 1, fichero);
		}

		/* Cerramos "empleados.dat". */
		fclose(fichero);

		if(encontrado == 0){
			printf("\n\nNo existe ningun empleado con edad %d.", filtroEdad);
		}
	}
}

Este es un método muy básico para realizar búsquedas dentro de una fichero, y cumple con el propósito de imprimir solo los datos de los empleados que tienen determinada característica.

fseek

Por último tenemos la función fseek, que nos permitirá acceder directamente a un registro de nuestro archivo. Esto nos puede interesar para leer o para modificar ese registro en concreto. Lo que hace la función fseek es situar el puntero del fichero  en una posición determinada de este. Su sintaxis general es:

fseek(puntero, tamaño_salto, desde);

Donde puntero es la variable puntero del fichero, tamaño_salto es el tamaño en bytes del salto realizado desde la posición desde. El argumento desde tiene tres posibles valores constantes, que son:

Constante Valor Descripción
SEEK_SET 0 Salta desde el principio del fichero.
SEEK_CUR 1 Salta desde la posición actual.
SEEK_END 2 Salta desde el final del fichero.

Siguiendo con el ejemplo anterior, veamos un ejemplo que imprimirá la estructura de datos del empleado que se encuentra en tercer lugar dentro del archivo binario empleados.dat.

#include <stdio.h>

struct datos_empleado {
	char nombre[30];
	int edad;
};

int main(void) {

	/* Declaramos la variable fichero como puntero a FILE. */
	FILE *fichero;

	struct datos_empleado empleado;

	/* Abrimos "empleados.dat" en modo texto y
	 * guardamos su direccion en el puntero. */
	fichero = fopen("empleados.dat", "rb");

	if (fichero == NULL) {
		printf("Error: No se ha podido crear el fichero empleados.dat");
	} else {

		/* Nos posicionamos en la tercera estructura empleado
		 * desde el inicio del fichero "empleados.dat". */
		fseek(fichero, 2 * sizeof(empleado), SEEK_SET);

		/* Leemos esa estructura. */
		fread(&empleado, sizeof(empleado), 1, fichero);

		/* La imprimimos. */
		printf("\n\nNombre: %s", empleado.nombre);
		printf("\nEdad: %d", empleado.edad);

		/* Cerramos "empleados.dat". */
		fclose(fichero);
	}
}

Voy a explicar la línea:

fseek(fichero, 2 * sizeof(empleado), SEEK_SET);

El parámetro fichero, tal y como ya se ha explicado en varias ocasiones en ejemplos anteriores, indica el puntero del archivo que vamos a leer. Lo tenemos que tener abierto mediante la función fopen previamente. Antes de explicar el segundo parámetro voy a hablar sobre el tercer parámetro, SEEK_SET, que sirve para decirle a la función que vamos a mover el puntero del archivo hasta una determinada distancia desde el inicio del fichero. La cuestión ahora es ¿a qué distancia voy a mover el puntero desde el inicio del fichero? En el ejemplo anterior hemos dicho que nos interesa leer el tercer registro del fichero, bien, pues necesitaremos dar un salto desde el inicio hasta los dos primeros registros, y para ello deberemos indicar esa distancia o tamaño en bytes. Como son dos estructuras las que tenemos que saltar debemos multiplicar por dos el tamaño de una estructura, y para averiguar dinámicamente el tamaño de nuestra estructura empleado podemos utilizar la función sizeof(empleado). Así pues, 2*sizeof(empleado) nos permite saltar dos registros desde el inicio (SEEK_SET) del fichero (fichero), con lo que la llamada a la función fseek hace que el puntero se encuentre al principio del tercer registro. Por último se llama a la función fread que lee el tercer registro y luego lo imprime.

Otro ejemplo de uso de la función fseek es utilizando la constante SEEK_CUR, que realiza un salto desde la posición actual, o lo que es lo mismo, salta un registro. Esto nos puede servir si lo que queremos es imprimir registros uno sí , uno no, por ejemplo los impares, veamos un ejemplo:

#include <stdio.h>

struct datos_empleado {
	char nombre[30];
	int edad;
};

int main(void) {

	/* Declaramos la variable fichero como puntero a FILE. */
	FILE *fichero;

	struct datos_empleado empleado;

	/* Abrimos "empleados.dat" en modo texto y
	 * guardamos su direccion en el puntero. */
	fichero = fopen("empleados.dat", "rb");

	if (fichero == NULL) {
		printf("Error: No se ha podido crear el fichero empleados.dat");
	} else {

		/* Leemos el primer registro. */
		fread(&empleado, sizeof(empleado), 1, fichero);

		while (!feof(fichero)) {
			/* Imprimimos los datos del registro. */
			printf("\n\nNombre: %s", empleado.nombre);
			printf("\nEdad: %d", empleado.edad);

			/* saltamos un registro desde la posicion actual. */
			fseek(fichero, 1 * sizeof(empleado), SEEK_CUR);

			/* Leemos el siguiente registro. */
			fread(&empleado, sizeof(empleado), 1, fichero);
		}

		/* Cerramos "empleados.dat". */
		fclose(fichero);
	}
}

Poder saltar con el puntero a una posición concreta del fichero nos permite realizar búsquedas mas precisas en menos tiempo.

2 opiniones en “Leer un fichero en C”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.