Lección 18 · 18 de 20
Archivos binarios: fwrite y fread · C
Guarde structs en un archivo binario con fwrite, léalos con fread y salte a un registro con fseek en C para modificarlo sin reescribir el archivo.

Un archivo binario guarda los bytes tal como están en memoria, sin convertirlos a texto. Se abre con una b en el modo ("wb", "rb", "r+b") y, a cambio de no poder leerse con un editor, es más compacto y más rápido, y cada registro ocupa siempre lo mismo.
`fwrite(items, sizeof(Product), 3, f)` escribe tres structs de una vez y devuelve cuántos ha escrito. Cada Product ocupa 32 bytes (16 del nombre, 4 del entero, 4 de relleno para alinear el double y 8 del double), así que el archivo mide 96 bytes, como confirma ftell tras fseek(f, 0, SEEK_END).
Como todos los registros miden lo mismo, el registro i empieza en el byte i * sizeof(Product). El modo `"r+b"` permite leer y escribir sin vaciar el archivo: fseek salta al registro 1, `fread` lo carga en p, se restan 5 ratones y, tras volver a la misma posición, fwrite lo sobrescribe. Es un acceso directo: no hace falta leer ni reescribir los demás registros.
rewind vuelve al principio y el bucle while (fread(&p, sizeof p, 1, f) == 1) lee registro a registro hasta el final. Al terminar, remove borra el archivo de prueba.
Los bytes dependen del ordenador: el tamaño de los tipos, el relleno y el orden de los bytes (*endianness*) pueden cambiar entre sistemas. Para intercambiar datos entre programas distintos, mejor texto o un formato con cada campo definido.
Código · main.c
#include <stdio.h>typedef struct { char name[16]; int stock; double price; } Product;int main(void) { Product items[] = {{"Keyboard", 14, 29.90}, {"Mouse", 40, 12.50}, {"Monitor", 6, 189.00}}; FILE *f = fopen("products.dat", "wb"); // "b": bytes, exactly as in memory if (!f) { perror("products.dat"); return 1; } size_t written = fwrite(items, sizeof(Product), 3, f); // 3 records at once fclose(f); printf("Wrote %zu records of %zu bytes\n", written, sizeof(Product)); f = fopen("products.dat", "r+b"); // read and write, without truncating if (!f) { perror("products.dat"); return 1; } fseek(f, 0, SEEK_END); printf("File size: %ld bytes\n", ftell(f)); Product p; fseek(f, 1 * sizeof(Product), SEEK_SET); // jump straight to record 1 if (fread(&p, sizeof p, 1, f) != 1) { fclose(f); return 1; } p.stock -= 5; // sell 5 mice fseek(f, 1 * sizeof(Product), SEEK_SET); // and overwrite that record fwrite(&p, sizeof p, 1, f); rewind(f); // back to the start: read them all while (fread(&p, sizeof p, 1, f) == 1) printf("%-9s %3d x %7.2f\n", p.name, p.stock, p.price); fclose(f); remove("products.dat"); return 0;}Salida · cc main.c -o main && ./main
Wrote 3 records of 32 bytes File size: 96 bytes Keyboard 14 x 29.90 Mouse 35 x 12.50 Monitor 6 x 189.00
Para recordar
- Modos con
b:"wb","rb","r+b". fwriteyfreaddevuelven cuántos elementos han procesado.fseek(f, i * sizeof(T), SEEK_SET)salta al registroi.- El formato binario depende del compilador y del sistema.
Ejercicio
Añada al final del archivo un cuarto producto abriéndolo en modo "ab" y escriba cuántos registros tiene ahora dividiendo el tamaño del archivo entre sizeof(Product).