C File I/O Cheat Sheet
Covers reading and writing text and binary files in C using fopen, fread and fwrite, formatted I/O, and file positioning functions.
Opening and Reading Text Files
Open a file stream and read it line by line.
#include <stdio.h>FILE *fp = fopen("data.txt", "r"); // Modes: r, w, a, r+, w+, a+if (fp == NULL) { perror("fopen failed"); return 1;}char line[256];while (fgets(line, sizeof(line), fp) != NULL) { printf("%s", line); // fgets keeps the trailing newline}fclose(fp);
Writing and Appending
Formatted and raw text output to a file.
FILE *out = fopen("output.txt", "w");fprintf(out, "Score: %d, Name: %s\n", 95, "Alice"); // Formatted writefputs("Another line\n", out); // Write a stringfputc('!', out); // Write a single charfclose(out);FILE *log = fopen("app.log", "a"); // Append mode: writes go to end of filefprintf(log, "Event logged\n");fclose(log);
Binary I/O
Reading and writing raw struct data with fread/fwrite.
struct Record { int id; double value; };struct Record r = {1, 3.14};FILE *bin = fopen("data.bin", "wb");fwrite(&r, sizeof(struct Record), 1, bin); // Write raw bytesfclose(bin);struct Record r2;FILE *in = fopen("data.bin", "rb");fread(&r2, sizeof(struct Record), 1, in); // Read raw bytes backfclose(in);
Functions & Positioning
Key stdio.h functions beyond basic read/write.
- fopen / fclose- Open and close a FILE stream; always check fopen's return value for NULL
- fread / fwrite- Binary block I/O; both return the number of items actually transferred
- fseek / ftell- fseek(fp, offset, SEEK_SET|SEEK_CUR|SEEK_END) moves the position; ftell reports it
- rewind(fp)- Resets the file position indicator back to the beginning of the stream
- feof / ferror- Check end-of-file or error state on a stream, typically after a read loop
- stdin/stdout/stderr- Predefined streams available without calling fopen
- fflush(fp)- Forces buffered output to be written immediately, e.g. before a crash-prone call
Controlling Stream Buffering
setvbuf lets you choose full, line, or no buffering per stream.
#include <stdio.h>char buf[8192];FILE *fp = fopen("out.log", "w");setvbuf(fp, buf, _IOFBF, sizeof(buf)); // full buffering with a custom buffer// _IOLBF: line-buffered (flushes on '\n'), _IONBF: unbufferedfprintf(fp, "first line\n");fflush(fp); // force pending data to the OS now, e.g. before a crash-prone callsetbuf(stdout, NULL); // shorthand for fully unbuffered stdoutfclose(fp);
Dynamic Line Reading with getline
POSIX getline grows its buffer automatically, avoiding fgets' fixed-size limit.
#include <stdio.h>#include <stdlib.h>FILE *fp = fopen("data.txt", "r");char *line = NULL;size_t cap = 0;ssize_t len;while ((len = getline(&line, &cap, fp)) != -1) { // line is realloc'd as needed; len includes the trailing '\n' if present printf("read %zd bytes: %s", len, line);}free(line); // caller owns the buffer getline allocatedfclose(fp);
Low-Level POSIX Descriptor I/O
open/read/write/close bypass the stdio buffer for direct system-call I/O.
#include <fcntl.h>#include <unistd.h>int fd = open("data.bin", O_RDONLY);if (fd == -1) { perror("open"); return 1;}char chunk[4096];ssize_t n;while ((n = read(fd, chunk, sizeof(chunk))) > 0) { write(STDOUT_FILENO, chunk, (size_t)n); // unbuffered write of what was read}close(fd);// Use this layer when you need raw file descriptors (pipes, sockets,// select/poll) rather than a buffered FILE* stream.
Random-Access Record Updates
Seeking to a fixed-size record's offset to read or rewrite it in place.
struct Record { int id; double balance; };void update_record(const char *path, long index, double new_balance) { FILE *fp = fopen(path, "r+b"); // r+b: read and write, must already exist if (!fp) return; long offset = index * (long)sizeof(struct Record); fseek(fp, offset, SEEK_SET); // jump directly to the Nth record struct Record rec; fread(&rec, sizeof(rec), 1, fp); rec.balance = new_balance; fseek(fp, offset, SEEK_SET); // rewind to the same spot before writing fwrite(&rec, sizeof(rec), 1, fp); fclose(fp);}
Extra stdio & POSIX Facilities
Less common but useful functions for temp files, renaming, and error reporting.
- tmpfile()- Creates and opens a unique temporary file that is auto-deleted when closed
- remove(path) / rename(old, new)- Delete or rename a file on disk; both return 0 on success
- freopen(path, mode, stream)- Redirects an existing stream (e.g. stdout) to a different file
- errno / strerror(errno)- After a failed I/O call, errno holds the OS error code; strerror renders it as text
- ftello / fseeko- (POSIX) 64-bit-safe variants of ftell/fseek for files larger than 2GB
- flock(fd, LOCK_EX)- (POSIX) advisory file locking to coordinate access across processes
- snprintf(buf, n, ...)- Formats into an in-memory buffer with the same syntax as fprintf, bounded and NUL-terminated
Always check the return value of fread/fwrite against the expected item count — a short read or write (e.g. from a full disk) is silently possible and easy to miss.