fread și fwrite în C

Scriu și citesc blocuri de octeți (tablouri, struct-uri) într-un fișier binar; fseek și ftell mută și arată poziția curentă.

Sintaxă
#include <stdio.h>
size_t fwrite(const void *ptr, size_t marime, size_t numar, FILE *f);
size_t fread(void *ptr, size_t marime, size_t numar, FILE *f);
int fseek(FILE *f, long offset, int deLa);
long ftell(FILE *f);
void rewind(FILE *f);

Ce face fread și fwrite

fwrite copiază în fișier numar elemente de câte marime octeți, exact cum sunt în memorie, fără conversie în text. fread face operația inversă. Sunt potrivite pentru tablouri de numere sau de struct; deschide fișierul cu "wb" / "rb".

Ambele returnează numărul de elemente (nu de octeți) transferate. Dacă fread returnează mai puțin decât ai cerut, fișierul s-a terminat sau a apărut o eroare (le deosebești cu feof și ferror).

Fiecare fișier deschis are o poziție curentă. fseek(f, offset, deLa) o mută: SEEK_SET de la început, SEEK_CUR de la poziția curentă, SEEK_END de la final. ftell spune poziția în octeți, iar rewind(f) revine la început. Un fișier binar scris pe un calculator poate să nu fie citit corect pe altul, dacă diferă dimensiunea tipurilor sau ordinea octeților.

Parametri

Parametru Descriere
ptr Adresa datelor de scris sau a memoriei în care se citește.
marime Dimensiunea unui element în octeți, de obicei sizeof.
numar Câte elemente se transferă.
offset, deLa La fseek: deplasarea în octeți și punctul de plecare (SEEK_SET, SEEK_CUR, SEEK_END).

Ce returnează

fread/fwrite: numărul de elemente transferate complet. fseek: 0 la succes. ftell: poziția curentă sau -1 la eroare.

Exemple

Exemplul 1: Salvarea unui tablou de struct-uri

Trei elevi sunt scriși cu un singur apel fwrite. fseek la final și ftell dau dimensiunea fișierului: 3 elemente × 20 de octeți. Apoi rewind și fread citesc totul înapoi.

#include <stdio.h>

struct Elev {
    char nume[16];
    int nota;
};

int main(void) {
    struct Elev clasa[] = {{"Ana", 9}, {"Mihai", 7}, {"Elena", 10}};
    FILE *f = fopen("elevi.bin", "w+b");
    if (f == NULL) return 0;

    size_t scrise = fwrite(clasa, sizeof clasa[0], 3, f);
    fseek(f, 0, SEEK_END);
    printf("Elemente scrise: %zu, dimensiune fișier: %ld octeți\n", scrise, ftell(f));

    rewind(f);
    struct Elev citite[3];
    size_t n = fread(citite, sizeof citite[0], 3, f);
    for (size_t i = 0; i < n; i++) {
        printf("%s: %d\n", citite[i].nume, citite[i].nota);
    }
    fclose(f);
    return 0;
}

Rezultat

Elemente scrise: 3, dimensiune fișier: 60 octeți
Ana: 9
Mihai: 7
Elena: 10

Exemplul 2: Citirea directă a unei înregistrări

Pentru că toate înregistrările au aceeași dimensiune, a treia începe la octetul 2 * sizeof(double). fseek sare direct acolo, fără să citească ce e înainte.

#include <stdio.h>

int main(void) {
    double temperaturi[] = {18.5, 21.0, 23.75, 19.25};
    FILE *f = fopen("temp.bin", "wb");
    if (f == NULL) return 0;
    fwrite(temperaturi, sizeof(double), 4, f);
    fclose(f);

    f = fopen("temp.bin", "rb");
    if (f == NULL) return 0;
    double t;
    fseek(f, 2 * (long)sizeof(double), SEEK_SET);
    if (fread(&t, sizeof t, 1, f) == 1) {
        printf("Ziua 3: %.2f °C (poziția acum: %ld)\n", t, ftell(f));
    }
    if (fread(&t, sizeof t, 1, f) == 1) {
        printf("Ziua 4: %.2f °C\n", t);
    }
    if (fread(&t, sizeof t, 1, f) != 1 && feof(f)) {
        printf("Nu mai sunt date.\n");
    }
    fclose(f);
    return 0;
}

Rezultat

Ziua 3: 23.75 °C (poziția acum: 24)
Ziua 4: 19.25 °C
Nu mai sunt date.

Greșeli frecvente

  • Scrii cu fwrite un struct care conține pointeri (de exemplu char *nume). În fișier ajunge adresa, nu textul, iar la citire adresa nu mai are sens.
  • Compari rezultatul lui fread cu numărul de octeți. Funcția returnează numărul de elemente: fread(v, sizeof(int), 10, f) returnează cel mult 10, nu 40.
  • Deschizi fișierul binar fără b pe Windows. Octeții 0x0A pot fi transformați în 0x0D 0x0A și datele se strică.

Comenzi înrudite

  • fopen și fclose: Deschide un fișier pentru citire, scriere sau adăugare și returnează un FILE *; fclose îl închide și salvează datele rămase.
  • struct: Grupează mai multe valori de tipuri diferite într-un singur tip nou, de exemplu numele, vârsta și orașul unei persoane.
  • sizeof: Operatorul care spune câți octeți ocupă un tip sau o variabilă; calculat de compilator, rezultatul are tipul size_t.
  • memcpy: Copiază n octeți dintr-o zonă de memorie în alta; zonele nu au voie să se suprapună.
  • fprintf: Scrie text formatat într-un fișier sau în stderr, cu același șablon ca printf.