Why files at all
Everything a program holds in variables lives in RAM and disappears the moment the program ends. A file stores data on secondary storage, so it survives — which is what makes it possible to enter records today and read them tomorrow.
Files also let data move between programs, hold far more than would fit in memory, and provide a record that can be backed up.
Check for NULL every single time
A file may be missing, may be locked by another program, or may be in a folder you cannot write to. If fopen fails it returns NULL, and using a NULL file pointer crashes the program. The check costs two lines and turns an unexplained crash into a clear message — and it is worth a mark whenever file handling appears in an exam.
The modes
The second argument to fopen says what you intend to do, and choosing the wrong one is destructive.
| Mode | Means | If the file exists | If it does not |
|---|---|---|---|
| "r" | read | opens it | fails, returns NULL |
| "w" | write | erases everything in it | creates it |
| "a" | append | adds at the end | creates it |
| "r+" | read and write | opens it | fails |
| "w+" | read and write | erases it first | creates it |
| "a+" | read and append | adds at the end | creates it |
"w" destroys the file immediately
Opening an existing file in "w" mode truncates it to zero length at the moment it is opened — before you have written anything, and whether or not you go on to write anything at all. A program intended to add a record to a file, opened with "w" instead of "a", deletes every previous record. This is the single most costly mistake in the chapter.
Reading and writing
Each input and output function has a file version, taking the file pointer as an extra argument.
fprintf(fp, "%d\n", marks) writes formatted output. fscanf(fp, "%d", &marks) reads it back. fgets(line, 100, fp) reads a whole line including spaces. fputc and fgetc handle single characters.
Knowing when the file has ended matters. feof(fp) reports end of file, but it becomes true only after a read has already failed — so a loop written as while (!feof(fp)) processes the last record twice. The reliable pattern tests the return value of the read itself: while (fscanf(fp, "%d", &n) == 1) { … }.
Write a program that appends a student's name and mark to a file, then reads the whole file back and prints it.
fp = fopen("marks.txt", "a");and check for NULL."a" adds to the end. "w" would erase every record already stored.fprintf(fp, "%s %d\n", name, marks); fclose(fp);The newline separates records. Closing flushes the buffer to disk — without it the data may never be written.- Reopen for reading:
fp = fopen("marks.txt", "r");and check for NULL again.A separate open, because the mode is different. while (fscanf(fp, "%s %d", name, &marks) == 2) { printf("%s: %d\n", name, marks); }Testing that fscanf returned 2 — the number of items it was asked for — ends the loop exactly at the end of the data.fclose(fp);Every open needs a matching close, whichever mode was used.
Open with "a" to append, close, reopen with "r", and loop while fscanf returns the expected count.
Closing, and why it matters
Writes are buffered: the data is collected in memory and written to disk in blocks, because writing one byte at a time would be extremely slow. fclose flushes whatever is still buffered and releases the file.
A program that ends without closing may therefore lose its most recent writes entirely — the data was in the buffer, never on the disk. Open files also consume a limited operating-system resource, so a long-running program that never closes anything eventually cannot open any more.
Before you leave this chapter
- Files persist after the program ends; variables do not.
- fopen returns NULL on failure — check it every time.
- "r" reads, "w" ERASES then writes, "a" appends. Choosing "w" instead of "a" destroys the file.
- fprintf/fscanf are the file versions of printf/scanf, taking the file pointer first.
- Test the return value of the read rather than feof, and always fclose to flush the buffer.
Text files and binary files
The files in this chapter are text files: the number 12345 is stored as five characters, readable in any editor, and portable between systems. That readability is their advantage and their cost — five bytes for a number that would fit in two, and every value must be converted on the way in and out.
A binary file stores the raw bytes of the data instead, written with fwrite and read with fread. It is smaller and faster because no conversion happens, and an entire record can be written in one call. But it is unreadable in an editor and may not transfer between machines that store numbers differently.
| Text file | Binary file | |
|---|---|---|
| Stores | characters | raw bytes |
| Readable in an editor | yes | no |
| Size | larger | smaller |
| Speed | slower — values are converted | faster — no conversion |
| Functions | fprintf, fscanf, fgets | fwrite, fread |
| Portable between systems | yes | not always |
Which to choose
Use a text file when a person may need to read or edit it, when it must be shared between different programs or systems, or when it is a configuration or log. Use a binary file for large volumes of numeric data where size and speed matter and only your own program will read it. For the quantities in a school project, text is almost always the right answer.