File Handling · intermediate · ~10 min
Open files for reading/writing/appending.
fopen(path, mode) opens a file and returns a FILE * you use with fread, fwrite, fprintf, fgets, etc. The mode is "r", "w", "a", with optional + (read+write) and b (binary on Windows; no-op on Unix). Every successful fopen needs a matching fclose.
Files are how programs persist data and exchange it with other programs and humans. Reading configuration, writing logs, processing CSV, streaming a download to disk — all build on fopen + a tiny family of stdio functions. Mastering them is the difference between toy programs and real tools.
Buffered I/O. stdio buffers your reads and writes in a small (~4 KB) memory buffer — flushed automatically on fclose, on \n for line-buffered streams, or explicitly via fflush. Modes. r (read, file must exist), w (write, truncate), a (append, create if needed). Add + to allow both read and write. Error checking. fopen returns NULL on failure; check errno for the reason (perror("fopen") prints a friendly message).
FILE *fp = fopen("data.txt", "r");
if (!fp) { perror("fopen"); return 1; }
char line[256];
while (fgets(line, sizeof line, fp)) {
/* process line — note it includes the trailing \n if it fit */
}
fclose(fp);
FILE *fopen(const char *path, const char *mode) returns a stream pointer or NULL. Common modes:
"r" read text, error if missing."w" write text, truncates or creates."a" append text, creates if missing."b" for binary ("rb", "wb").Always pair with fclose. Check both fopen and fclose return values.
FILE *f = fopen("log.txt", "a");
if (!f) { perror("fopen"); return 1; }
fprintf(f, "%s\n", msg);
if (fclose(f) != 0) perror("fclose");
fclose — a successful write isn't actually flushed to disk until close."b" on Windows: text-mode translates \n ↔ \r\n.When fopen returns NULL, perror (or strerror(errno)) tells you why — usually 'No such file' or 'Permission denied'. If you're getting unexpected content, check the mode: "r" on a directory still 'succeeds' on some Unixes, then fread fails weirdly. Use absolute paths until your relative paths are correct.
Every fopen needs a fclose, even on error paths — goto cleanup patterns are common in C exactly for this reason. Forgetting fclose leaks file descriptors; on a busy server, you'll run out of them and fopen will start returning 'Too many open files'.
Reading config files, parsing CSV, processing log files line by line, writing reports, saving game state, importing/exporting data.
fgets. 3. Copy a file byte-by-byte using fread/fwrite. 4. Open the same file twice and compare positions with ftell.fopen opens, fclose closes; in between you use fread / fgets / fprintf / fwrite. Check the return, choose the right mode, and never forget to close — and you have a robust file pipeline for any text or binary data.