que tus datos sobrevivan al programa.
Incluye 4 ejercicios con pistas y solución y un quiz de 5 preguntas. Tiempo estimado: 80 minutos.
1. El ciclo: abrir → usar → cerrar
FILE *f = fopen("datos.txt", "r");
if (f == NULL) { // SIEMPRE comprobar
perror("datos.txt"); // imprime el motivo ("No such file...")
return 1;
}
... leer / escribir ...
fclose(f);
Modos de fopen
| Modo | Significado | Si no existe | Si existe |
|---|---|---|---|
"r" |
lectura | falla (NULL) | lee desde el inicio |
"w" |
escritura | lo crea | lo borra (trunca) |
"a" |
añadir al final | lo crea | conserva y añade |
"r+" |
leer y escribir | falla | conserva |
"rb", "wb"... |
igual, en binario |
En Windows usa
bpara ficheros binarios; en texto,\r\nse traduce a\nautomáticamente.
2. Ficheros de texto
| Función | Para |
|---|---|
fprintf(f, "%d %s\n", n, s) |
escribir con formato |
fscanf(f, "%d", &n) |
leer con formato (devuelve cuántos leyó) |
fgets(buf, tam, f) |
leer una línea (devuelve NULL al acabar) |
fputs(s, f) / fputc(c, f) |
escribir cadena / carácter |
fgetc(f) |
leer un carácter (devuelve int; EOF al final) |
Bucles de lectura típicos:
// línea a línea
char linea[256];
while (fgets(linea, sizeof linea, f)) { ... }
// valores
int x;
while (fscanf(f, "%d", &x) == 1) { ... }
// carácter a carácter
int ch; // int, no char (por EOF)
while ((ch = fgetc(f)) != EOF) { ... }
⚠️ No hagas
while (!feof(f)):feofsolo se activa después de intentar leer más allá del final, y procesarás una iteración de más. Comprueba el valor de retorno de la lectura.
Truco de fscanf para CSV
fscanf(f, " %31[^;];%d", nombre, &edad): %[^;] lee hasta el ;; el espacio inicial salta saltos de línea pendientes.
3. Ficheros binarios
Vuelcas bytes tal cual están en memoria: rápido y compacto.
fwrite(arr, sizeof arr[0], n, f); // escribe n elementos
size_t leidos = fread(arr, sizeof arr[0], n, f); // devuelve cuántos leyó
Ventajas: velocidad y simplicidad. Inconvenientes: no se puede abrir con un editor, y no es portable entre sistemas (tamaños, endianness, padding). Perfecto para cachés y datos propios; mal para intercambiar.
4. Posición dentro del fichero
ftell(f) (dónde estoy), fseek(f, desplazamiento, SEEK_SET/SEEK_CUR/SEEK_END), rewind(f).
Truco para saber el tamaño: fseek(f, 0, SEEK_END); long n = ftell(f); rewind(f);
5. Entrada/salida estándar son ficheros
stdin, stdout, stderr son FILE * ya abiertos. Los errores van a stderr: fprintf(stderr, "Error: ...\n"). Así se pueden redirigir por separado (prog > salida.txt).
6. Argumentos de línea de comandos
int main(int argc, char *argv[]) {
if (argc < 2) { fprintf(stderr, "Uso: %s fichero\n", argv[0]); return 1; }
FILE *f = fopen(argv[1], "r");
Buenas prácticas
- Comprueba todos los
fopen; cierra todo lo que abras. - Guarda de forma atómica lo importante: escribe a un fichero temporal y luego renómbralo (
rename). - Texto > binario salvo que necesites velocidad: puedes depurar con cualquier editor.
Prueba esto
- Programa
copiar origen destinoque copie un fichero byte a byte. - Cuenta las líneas de un fichero con
fgets(cuidado con líneas más largas que el buffer). - Guarda y carga un array de
doubleen binario y compara.
Ejercicios prácticos del módulo 10
Programar se aprende programando: intenta cada ejercicio antes de mirar las pistas, y usa la solución solo para comparar. Marca los que resuelvas y tu progreso se guarda en este navegador.
10.1 Escribir y volver a leer
Entrada de ejemplo
6 1 2 3 4 5 6
Salida esperada
suma=21
Tu punto de partida
#include <stdio.h>
int main(void) {
int n, x;
scanf("%d", &n);
// TODO 1: abre "datos.txt" en modo "w", escribe los n numeros, cierra
// TODO 2: abre "datos.txt" en modo "r", suma con fscanf hasta EOF, cierra
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
fopen devuelve NULL si falla: compruebalo siempre.
Pista 2
fprintf(f, ...) es como printf pero escribe en el fichero f.
Pista 3
fscanf devuelve cuantos valores leyo: while (fscanf(f, "%d", &x) == 1) { ... }
Ver solución (inténtalo antes)
#include <stdio.h>
int main(void) {
int n, x;
scanf("%d", &n);
FILE *f = fopen("datos.txt", "w");
if (!f) return 1;
for (int i = 0; i < n; i++) {
scanf("%d", &x);
fprintf(f, "%d\n", x);
}
fclose(f);
f = fopen("datos.txt", "r");
if (!f) return 1;
long long suma = 0;
while (fscanf(f, "%d", &x) == 1) suma += x;
fclose(f);
printf("suma=%lld\n", suma);
return 0;
}10.2 Estilo wc: lineas, palabras, caracteres
Entrada de ejemplo
(ninguna)
Salida esperada
lineas=2 palabras=5 caracteres=25
Tu punto de partida
#include <stdio.h>
#include <ctype.h>
int main(void) {
FILE *f = fopen("texto.txt", "r");
// TODO: comprueba f, recorre con fgetc, cuenta, cierra e imprime
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
fgetc devuelve un int (no un char) porque EOF es -1: guarda el resultado en un int.
Pista 2
Para contar palabras, recuerda si 'estas dentro de una palabra': al pasar de blanco a no-blanco, cuenta una.
Ver solución (inténtalo antes)
#include <stdio.h>
#include <ctype.h>
int main(void) {
FILE *f = fopen("texto.txt", "r");
if (!f) {
printf("error: no se puede abrir texto.txt\n");
return 0;
}
int ch, lineas = 0, palabras = 0, chars = 0, dentro = 0;
while ((ch = fgetc(f)) != EOF) {
chars++;
if (ch == '\n') lineas++;
if (isspace(ch)) dentro = 0;
else if (!dentro) { dentro = 1; palabras++; }
}
fclose(f);
printf("lineas=%d palabras=%d caracteres=%d\n", lineas, palabras, chars);
return 0;
}10.3 Ficheros binarios con structs
Entrada de ejemplo
2 Ana 30 Luis 25
Salida esperada
Ana (30) Luis (25)
Tu punto de partida
#include <stdio.h>
typedef struct {
char nombre[32];
int edad;
} Persona;
int main(void) {
Persona p[20], q[20];
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%31s %d", p[i].nombre, &p[i].edad);
// TODO: fwrite(p, sizeof(Persona), n, f) en "gente.bin" (modo "wb")
// TODO: fread(q, sizeof(Persona), n, f) desde "gente.bin" (modo "rb")
// TODO: imprime q
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
Modos binarios: "wb" y "rb" (la b importa en Windows).
Pista 2
fwrite/fread devuelven cuantos ELEMENTOS procesaron: comparalo con n para detectar errores.
Ver solución (inténtalo antes)
#include <stdio.h>
typedef struct {
char nombre[32];
int edad;
} Persona;
int main(void) {
Persona p[20], q[20];
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%31s %d", p[i].nombre, &p[i].edad);
FILE *f = fopen("gente.bin", "wb");
if (!f) return 1;
fwrite(p, sizeof(Persona), n, f);
fclose(f);
f = fopen("gente.bin", "rb");
if (!f) return 1;
size_t leidos = fread(q, sizeof(Persona), n, f);
fclose(f);
for (size_t i = 0; i < leidos; i++) printf("%s (%d)\n", q[i].nombre, q[i].edad);
return 0;
}10.4 Agenda persistente
Entrada de ejemplo
add ana 111 add luis 222 list salir
Salida esperada
ana: 111 luis: 222
Tu punto de partida
#include <stdio.h>
#include <string.h>
int main(void) {
char cmd[16], nombre[64], tel[32];
while (scanf("%15s", cmd) == 1 && strcmp(cmd, "salir") != 0) {
if (strcmp(cmd, "add") == 0) {
scanf("%63s %31s", nombre, tel);
// TODO: fopen("agenda.csv", "a"), fprintf "%s;%s\n", fclose
} else if (strcmp(cmd, "list") == 0) {
// TODO: si el fichero existe, lee cada linea con fscanf(" %63[^;];%31s") e imprime "nombre: tel"
}
}
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
Modo "a" (append) escribe al final sin borrar lo anterior.
Pista 2
%63[^;] lee todo hasta el siguiente ';' (como un %s que se detiene en ;). Luego el ; literal del formato lo consume. Pon un ESPACIO antes del % (" %63[^;]..."): salta el que quedo de la linea anterior.
Pista 3
Abre y cierra el fichero dentro de cada comando: asi los datos quedan guardados aunque el programa muera.
Ver solución (inténtalo antes)
#include <stdio.h>
#include <string.h>
int main(void) {
char cmd[16], nombre[64], tel[32];
while (scanf("%15s", cmd) == 1 && strcmp(cmd, "salir") != 0) {
if (strcmp(cmd, "add") == 0) {
scanf("%63s %31s", nombre, tel);
FILE *f = fopen("agenda.csv", "a");
if (f) {
fprintf(f, "%s;%s\n", nombre, tel);
fclose(f);
}
} else if (strcmp(cmd, "list") == 0) {
FILE *f = fopen("agenda.csv", "r");
if (f) {
while (fscanf(f, " %63[^;];%31s", nombre, tel) == 2)
printf("%s: %s\n", nombre, tel);
fclose(f);
}
}
}
return 0;
}Quiz del módulo 10
5 preguntas para comprobar que has fijado los conceptos. Las opciones se barajan cada vez.