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.
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.
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.
#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);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.
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);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?
Why this lesson exists
Syllabus mapping
Input/ Output Files - Review and Further Study
Maps to course outcome CO3.