Kodewick
Lesson 27 of 30Contents

Files

Lesson 27 of 30 · Part 6 · Going further · about 15 minutes

Everything a program keeps in variables is gone when it ends. To keep something for next time, write it to a file. This program writes one line to hello.txt, closes it, then opens it again and reads the line back:

main.c
1#include <stdio.h>2 3int main(void) {4    FILE *f = fopen("hello.txt", "w");5    fprintf(f, "Hello, file!\n");6    fclose(f);7 8    char line[100];9    f = fopen("hello.txt", "r");10    fgets(line, sizeof line, f);11    fclose(f);12    printf("Read back: %s", line);13    return 0;14}
Output
Read back: Hello, file!

fopen opens a file and gives back a FILE *, a pointer to what C knows about the open file. Every other file function takes that pointer. fprintf works just like printf, except that its first argument says which file to write to.

On this page, each example runs in a small folder of its own in your browser. A file it writes is there until the program ends, then it's gone. That's why every example here writes its file first, then reads it, all in the same program.

Opening and closing

The second argument of fopen is the mode: what you want to do with the file.

ModeWhat it doesIf the file doesn't existIf it already exists
"r"readfopen gives back NULLreads from the start
"w"writecreates itempties it first
"a"append (add to the end)creates itwrites after what's there

When you're done, call fclose. What you write with fprintf first waits in memory and goes into the file in bigger pieces; fclose makes sure all of it gets there, and lets go of the file. Close every file you open.

Guess first

What does this print?

1#include <stdio.h>2 3int main(void) {4    FILE *f = fopen("day.txt", "w");5    fprintf(f, "Monday\n");6    fclose(f);7    f = fopen("day.txt", "w");8    fprintf(f, "Tuesday\n");9    fclose(f);10 11    char line[100];12    f = fopen("day.txt", "r");13    while (fgets(line, sizeof line, f) != NULL) {14        printf("%s", line);15    }16    fclose(f);17    return 0;18}

When fopen fails

fopen doesn't always work. Open a file that doesn't exist for reading, or get its name wrong, and it gives back NULL. Always check before you use the pointer:

main.c
1#include <stdio.h>2 3int main(void) {4    FILE *f = fopen("plans.txt", "r");5    if (f == NULL) {6        printf("Can't open plans.txt\n");7        return 1;8    }9    printf("Opened it\n");10    fclose(f);11    return 0;12}
Output
Can't open plans.txt

return 1; ends the program early. Any number other than 0 tells the system that something went wrong.

Without the check, the NULL goes on into the next function. In real C, the program usually crashes there. Our engine stops on that line and tells you why:

main.c
1#include <stdio.h>2 3int main(void) {4    char line[100];5    FILE *f = fopen("plans.txt", "r");6    fgets(line, sizeof line, f);7    printf("%s", line);8    return 0;9}
Output
fgets on a NULL FILE *

What happened: Stopped at line 6. This pointer is NULL: nothing can be read or written through it.

Reading line by line

fgets(line, sizeof line, f) reads one line into the char array line, at most sizeof line - 1 characters, and adds the '\0'. When there's nothing left, it gives back NULL. So the usual loop is "keep reading until fgets gives NULL":

main.c
1#include <stdio.h>2 3int main(void) {4    FILE *f = fopen("shopping.txt", "w");5    fprintf(f, "milk\n");6    fprintf(f, "eggs\n");7    fprintf(f, "%d apples\n", 6);8    fclose(f);9 10    char line[100];11    int count = 0;12    f = fopen("shopping.txt", "r");13    while (fgets(line, sizeof line, f) != NULL) {14        count++;15        printf("%d: %s", count, line);16    }17    fclose(f);18    printf("%d lines\n", count);19    return 0;20}
Output
1: milk
2: eggs
3: 6 apples
3 lines

Notice printf("%d: %s", count, line) has no \n. fgets keeps the '\n' at the end of the line, so line already ends with one. Press Step and look at the cells of line after each fgets: the '\n' is there, right before the '\0'.

Reading numbers

fgets gives you text. For numbers, fscanf is easier: it's scanf (lesson 3) for a file. It gives back how many values it read, so == 1 means "got one number". At the end of the file it can't read any more, and the loop stops.

main.c
1#include <stdio.h>2 3int main(void) {4    FILE *f = fopen("steps.txt", "w");5    int week[5] = {4200, 6100, 5300, 8000, 7200};6    for (int i = 0; i < 5; i++) {7        fprintf(f, "%d\n", week[i]);8    }9    fclose(f);10 11    int steps;12    int total = 0;13    int days = 0;14    f = fopen("steps.txt", "r");15    while (fscanf(f, "%d", &steps) == 1) {16        total += steps;17        days++;18    }19    fclose(f);20    printf("%d days, %d steps, %.1f a day\n", days, total, (double)total / days);21    return 0;22}
Output
5 days, 30800 steps, 6160.0 a day

fscanf skips spaces and new lines before a number, so it doesn't matter whether the numbers are on one line or many.

A name and a number on each line

fscanf can read several values at once. With "%s %d", %s reads one word into a char array (up to the next space), then %d reads a number. It gives back 2 when it got both:

main.c
1#include <stdio.h>2#include <string.h>3 4int main(void) {5    FILE *f = fopen("scores.txt", "w");6    fprintf(f, "Ana 87\n");7    fprintf(f, "Ben 92\n");8    fprintf(f, "Chloe 78\n");9    fclose(f);10 11    char name[20];12    int score;13    char best_name[20] = "";14    int best = -1;15    f = fopen("scores.txt", "r");16    while (fscanf(f, "%19s %d", name, &score) == 2) {17        if (score > best) {18            best = score;19            strcpy(best_name, name);20        }21    }22    fclose(f);23    printf("Top score: %s, %d\n", best_name, best);24    return 0;25}
Output
Top score: Ben, 92

Two things to see here. name has no &: an array's name is already an address (lesson 17). And %19s reads at most 19 characters, so a long name can't go past the end of name[20].

Adding to a file

Mode "a" keeps what's in the file and writes after it. A log, for example, should grow, not start over every time:

main.c
1#include <stdio.h>2 3int main(void) {4    FILE *f = fopen("log.txt", "w");5    fprintf(f, "09:00 opened\n");6    fclose(f);7 8    f = fopen("log.txt", "a");9    fprintf(f, "12:30 lunch break\n");10    fprintf(f, "18:00 closed\n");11    fclose(f);12 13    char line[100];14    f = fopen("log.txt", "r");15    while (fgets(line, sizeof line, f) != NULL) {16        printf("%s", line);17    }18    fclose(f);19    return 0;20}
Output
09:00 opened
12:30 lunch break
18:00 closed

The first fopen uses "w", so the log starts empty. The second uses "a", so the two new lines go after 09:00 opened.

Tip: In "w" and "a" you can only write, and in "r" you can only read. To read a file you just wrote, close it and open it again with "r", as every example here does.

Practice

Write the code, then press Check. A hint costs nothing; the answer is there when you need it.

Exercise 1

Number the lines

The program writes three lines to todo.txt. Read them back and print each one with its number in front, like 1. buy milk.

It should print
1. buy milk
2. call mom
3. pay rent
exercise1.c
1#include <stdio.h>2 3int main(void) {4    FILE *f = fopen("todo.txt", "w");5    fprintf(f, "buy milk\n");6    fprintf(f, "call mom\n");7    fprintf(f, "pay rent\n");8    fclose(f);9 10    return 0;11}
Output
Press Run or Ctrl+Enter
Exercise 2

Add up the prices

The program writes some prices to prices.txt. Read them back with fscanf, then print the count and the total, like 4 items, 23.75 in all.

It should print
4 items, 23.75 in all
exercise2.c
1#include <stdio.h>2 3int main(void) {4    FILE *f = fopen("prices.txt", "w");5    fprintf(f, "3.50\n8.25\n2.00\n10.00\n");6    fclose(f);7 8    return 0;9}
Output
Press Run or Ctrl+Enter
Exercise 3

The lost line

The program should save two notes, but only the second one is left in the file. Fix it so it prints both lines.

It should print
Rained all day
Sunny at last
exercise3.c
1#include <stdio.h>2 3int main(void) {4    FILE *f = fopen("diary.txt", "w");5    fprintf(f, "Rained all day\n");6    fclose(f);7    f = fopen("diary.txt", "w");8    fprintf(f, "Sunny at last\n");9    fclose(f);10 11    char line[100];12    f = fopen("diary.txt", "r");13    while (fgets(line, sizeof line, f) != NULL) {14        printf("%s", line);15    }16    fclose(f);17    return 0;18}
Output
Press Run or Ctrl+Enter

What you learned

  • fopen(name, mode) opens a file and gives back a FILE *; "r" reads, "w" writes (emptying the file first), "a" adds to the end.
  • fopen gives back NULL when it can't open the file: check with if (f == NULL) before using it.
  • fprintf(f, ...) writes like printf; fclose(f) makes sure everything is written and lets go of the file.
  • fgets(line, sizeof line, f) reads one line, keeping its '\n', and gives NULL at the end of the file.
  • fscanf(f, "%d", &x) reads values like scanf and gives back how many it read: loop while it's == 1 (or == 2 for two values).