Skip to content
Laviyx Learn

Try

C course menu

File Handling in C: fopen, fprintf, fgets, fread, fwrite and fclose

Learn file input and output in C: opening files with fopen and its modes, checking for errors, writing text with fprintf and fputs, reading lines with fgets, reading formatted data with fscanf, binary files with fwrite and fread, fseek and ftell, and always closing files.

Advanced10 min readUpdated
On this page

Programs often need data that outlives a single run: logs, settings, records. The C standard library handles files through a FILE * pointer, using functions very similar to the console ones you already know.

Opening and closing#

fopen(path, mode) opens a file and returns a FILE *, or NULL on failure (the file does not exist, permission denied, and so on). fclose closes it and flushes any buffered output:

C
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
    perror("data.txt");          /* prints the reason, e.g. "No such file or directory" */
    return 1;
}
/* ... use fp ... */
fclose(fp);
ModeMeaningIf the file existsIf it does not
"r"ReadRead from the startFails (NULL)
"w"WriteErased firstCreated
"a"AppendWrites go to the endCreated
"r+"Read and writeKeptFails
"w+"Read and writeErasedCreated

Add b ("rb", "wb") for binary files. On Windows, text mode converts \n to \r\n when writing, and binary mode does not; on Linux and macOS the two are the same.

Writing and reading text#

C
#include <stdio.h>

int main(void) {
    FILE *out = fopen("marks.txt", "w");
    if (out == NULL) { perror("marks.txt"); return 1; }
    fprintf(out, "Asha 87\n");
    fprintf(out, "Ravi 92\n");
    fputs("Meena 78\n", out);
    fclose(out);

    FILE *in = fopen("marks.txt", "r");
    if (in == NULL) { perror("marks.txt"); return 1; }
    char line[100];
    int n = 0;
    while (fgets(line, sizeof line, in) != NULL) {
        printf("line %d: %s", ++n, line);     /* line already ends in '\n' */
    }
    fclose(in);
    return 0;
}
Output
line 1: Asha 87
line 2: Ravi 92
line 3: Meena 78

fgets returns NULL at the end of the file, which ends the loop. This is the standard way to read a text file line by line.

Reading formatted data#

fscanf reads values the way scanf does. Check its return value, the number of items it read, to stop at the end of the file or at bad data:

C
#include <stdio.h>

int main(void) {
    FILE *f = fopen("scores.txt", "w");
    if (f == NULL) return 1;
    fprintf(f, "Asha 87\nRavi 92\nMeena 78\n");
    fclose(f);

    f = fopen("scores.txt", "r");
    if (f == NULL) return 1;
    char name[32];
    int mark, total = 0, count = 0;
    while (fscanf(f, "%31s %d", name, &mark) == 2) {
        total += mark;
        count++;
    }
    fclose(f);
    printf("%d students, average %.2f\n", count, (double)total / count);
    return 0;
}
Output
3 students, average 85.67

For real-world input it is more robust to read each line with fgets and parse it with sscanf, so one bad line cannot leave the reader stuck mid-line.

Appending#

C
#include <stdio.h>

int main(void) {
    for (int run = 1; run <= 3; run++) {
        FILE *log = fopen("app.log", "a");
        if (log == NULL) return 1;
        fprintf(log, "run %d\n", run);
        fclose(log);
    }

    FILE *in = fopen("app.log", "r");
    if (in == NULL) return 1;
    int c;
    while ((c = fgetc(in)) != EOF) putchar(c);
    fclose(in);
    return 0;
}
Output
run 1
run 2
run 3

Binary files: fwrite and fread#

Text files are readable by people; binary files store the raw bytes of your data, which is compact and fast. fwrite(ptr, size, count, fp) and fread return how many items they processed:

C
#include <stdio.h>

typedef struct {
    int id;
    double price;
} Product;

int main(void) {
    Product items[] = {{1, 49.5}, {2, 120.0}, {3, 15.25}};

    FILE *f = fopen("products.bin", "wb");
    if (f == NULL) return 1;
    size_t written = fwrite(items, sizeof items[0], 3, f);
    fclose(f);
    printf("wrote %zu records\n", written);

    Product loaded[3];
    f = fopen("products.bin", "rb");
    if (f == NULL) return 1;
    size_t read = fread(loaded, sizeof loaded[0], 3, f);

    fseek(f, 0, SEEK_END);                   /* jump to the end */
    long size = ftell(f);                    /* position = file size */
    fclose(f);

    printf("read %zu records, file is %ld bytes\n", read, size);
    for (size_t i = 0; i < read; i++) printf("%d: %.2f\n", loaded[i].id, loaded[i].price);
    return 0;
}
Output
wrote 3 records
read 3 records, file is 48 bytes
1: 49.50
2: 120.00
3: 15.25

Each Product is 16 bytes here (4 for id, 4 of padding, 8 for price). Binary files written this way depend on the compiler's struct layout, type sizes and byte order, so they are not portable between different systems. Use text formats (CSV, JSON) to exchange data.

Moving around a file#

  • fseek(fp, offset, origin): move to a position. origin is SEEK_SET (start), SEEK_CUR (current position) or SEEK_END (end).
  • ftell(fp): the current position in bytes.
  • rewind(fp): back to the start.

Practice#

Practice exercise

Write a program that writes the numbers 1 to 10 to numbers.txt, one per line, then reads the file back and prints the count of even numbers and the sum of all numbers.

Show solution
C
#include <stdio.h>

int main(void) {
    FILE *f = fopen("numbers.txt", "w");
    if (f == NULL) { perror("numbers.txt"); return 1; }
    for (int i = 1; i <= 10; i++) fprintf(f, "%d\n", i);
    fclose(f);

    f = fopen("numbers.txt", "r");
    if (f == NULL) { perror("numbers.txt"); return 1; }
    int n, evens = 0, sum = 0;
    while (fscanf(f, "%d", &n) == 1) {
        sum += n;
        if (n % 2 == 0) evens++;
    }
    fclose(f);
    printf("evens %d, sum %d\n", evens, sum);
    return 0;
}
Output
evens 5, sum 55

Key takeaway#

Finished this lesson?