Unit 5 · Lesson 118 minAcademic review pending

Keeping data after the program ends

A file pointer is a handle to an open file. Open it, check it opened, use it, close it — and the check is the step everyone skips.

Choose explanation

After this lesson

You should be able to

  • Open a text file for reading, writing or appending and close it.
  • Detect and handle a failed open.
  • Read a file to the end using the return value of fscanf or fgets.
01

Four steps, in order

fopen returns a FILE * — a handle the library uses to track your position in the file. The mode string decides what you may do: "r" to read, "w" to write from empty, "a" to append to the end.

"w" empties an existing file the moment it opens, before you write anything. If you meant to add to a file and typed "w", the old contents are already gone. Use "a" when you mean to add.

Open, check, use, close
#include <stdio.h>

FILE *fp = fopen("marks.txt", "r");

if (fp == NULL) {                    /* never skip this */
    printf("Could not open marks.txt\n");
    return 1;
}

/* ... use fp ... */

fclose(fp);
02

Reading until the end

fscanf returns how many items it successfully read, so while (fscanf(fp, "%d", &n) == 1) stops cleanly at the end of the file or at the first unreadable value. That return value is what you loop on.

Do not loop on feof(fp). feof only becomes true after a read has already failed, so the last value gets processed twice. Test the read itself instead — this is the most common file-reading bug in first-year code.

Loop on the read, not on feof
int  n, total = 0, count = 0;

while (fscanf(fp, "%d", &n) == 1) {
    total += n;
    count++;
}

if (count > 0)
    printf("Average %.2f over %d values\n", (double) total / count, count);
03

Writing, and lines with spaces

fprintf works exactly like printf with a file pointer as the first argument, and fgets reads a whole line from a file just as it does from the keyboard. printf is really fprintf to a file called stdout, which is why the two look alike.

Always fclose. Output is buffered, so data can still be sitting in memory when the program ends abnormally; closing flushes it to disk.

Try it yourself

Write five marks to a text file, then read them back and print their average.

Need a hint?

That is two opens and two closes — you cannot read and write with the same handle here.

Check the worked solution

The file is closed after writing and reopened for reading, because "w" and "r" are different modes and the position is at the end after writing. Both opens are checked; the read loop tests fscanf's return value rather than feof, so the last mark is counted exactly once.

#include <stdio.h>

int main(void)
{
    int marks[5] = {72, 45, 91, 38, 66};
    int n, total = 0, count = 0;
    FILE *fp;

    fp = fopen("marks.txt", "w");
    if (fp == NULL) {
        printf("Cannot write marks.txt\n");
        return 1;
    }
    for (int i = 0; i < 5; i++)
        fprintf(fp, "%d\n", marks[i]);
    fclose(fp);

    fp = fopen("marks.txt", "r");
    if (fp == NULL) {
        printf("Cannot read marks.txt\n");
        return 1;
    }
    while (fscanf(fp, "%d", &n) == 1) {
        total += n;
        count++;
    }
    fclose(fp);

    printf("Read %d marks, average %.2f\n", count, (double) total / count);
    return 0;
}

Quick check

Why is while (!feof(fp)) a poor way to read a file?

Select an answer to check your thinking.

Why this lesson exists

Syllabus mapping

Input/ Output Files - Review and Further Study

Maps to course outcome CO3.