DMarlon Studio

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.

C · Archivos binarios: fwrite y fread en DMarlon Studio: el código del ejemplo y su salida en la terminal

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".
  • fwrite y fread devuelven cuántos elementos han procesado.
  • fseek(f, i * sizeof(T), SEEK_SET) salta al registro i.
  • 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).