Skip to content
FollowtheDamnCode

Reading and Writing Files

fprintf, fscanf, fgetc, and fputc.

5 minute read

Big idea

File functions mirror the ones you know: fprintf/fscanf are printf/scanf for files, and fgetc/fputc read and write one character at a time.

Formatted: fprintf and fscanf

They take the FILE * first, then exactly what printf/scanf take:

fprintf(f, "%s %d\n", name, score);
fscanf(f, "%s %d", name, &score);

fscanf returns how many values it read β€” 2 above when a line parsed fine, and EOF (a negative number) at the end of the file. That makes a natural loop condition:

Save scores, then total them from the file
#include <stdio.h>

int main(void) {
    FILE *f = fopen("scores.txt", "w");
    if (f == NULL) return 1;
    fprintf(f, "asha 88\n");
    fprintf(f, "ben 72\n");
    fprintf(f, "chen 95\n");
    fclose(f);

    f = fopen("scores.txt", "r");
    if (f == NULL) return 1;
    char name[20];
    int score, total = 0, count = 0;
    while (fscanf(f, "%19s %d", name, &score) == 2) {
        printf("%s: %d\n", name, score);
        total += score;
        count++;
    }
    fclose(f);
    printf("Average: %d\n", total / count);
    return 0;
}
output
asha: 88
ben: 72
chen: 95
Average: 85

Run and edit it in the app.

Watch it

Character by character: fgetc and fputc

fgetc(f) returns the next character β€” as an int, so it can also return EOF when the file runs out. Store it in an int, not a char:

Count lines and characters
#include <stdio.h>

int main(void) {
    FILE *f = fopen("poem.txt", "w");
    if (f == NULL) return 1;
    fputs("Roses are red\nViolets are blue\nC is fun\n", f);
    fclose(f);

    f = fopen("poem.txt", "r");
    if (f == NULL) return 1;
    int c, lines = 0, chars = 0;
    while ((c = fgetc(f)) != EOF) {
        chars++;
        if (c == '\n') lines++;
    }
    fclose(f);
    printf("%d lines, %d characters\n", lines, chars);
    return 0;
}
output
3 lines, 40 characters

Run and edit it in the app.

(c = fgetc(f)) != EOF does two things: stores the next character in c, then checks it. The brackets matter β€” without them, != would run first.

Appending

Opening in "a" mode adds to the end instead of erasing:

Predict: "w" then "a"
#include <stdio.h>

int main(void) {
    FILE *f = fopen("log.txt", "w");
    fputs("one\n", f);
    fclose(f);
    f = fopen("log.txt", "a");
    fputs("two\n", f);
    fclose(f);
    f = fopen("log.txt", "w");
    fputs("three\n", f);
    fclose(f);

    f = fopen("log.txt", "r");
    char line[20];
    while (fgets(line, sizeof line, f) != NULL) {
        printf("%s", line);
    }
    fclose(f);
    return 0;
}
What does it print? Show the answer
three

Run and edit it in the app.

The last "w" erased everything written before it.

Think of it like

A diary. fprintf writes a formatted entry; fscanf reads entries back in the same format; fgetc reads one letter at a time, like tracing the page with your finger.

πŸ•·οΈ Spidey-sense: My patrol log: fprintf(log, "%s %d\n", villain, hour); every night in "a" mode. One morning I opened it in "w" β€” three years of notes, gone. Now I trust "a".

Key terms

  • fprintf / fscanf β€” Formatted write / read on a file.
  • fgetc / fputc / fputs β€” Read a character / write a character / write a string.
  • EOF β€” "End of file": the value returned when there's nothing left to read.

Check yourself

Which writes formatted text to a file?

Why store fgetc's result in an int, not a char?

fscanf(f, "%d %d", &a, &b) returns 2 when…