Last 30 Days
No notifications
Real programs read configs, save scores, parse logs. C does this with the family of functions and a magical handle called FILE *.
FILE *fp = fopen("scores.txt", "r"); /* open for reading */
if (fp == NULL) {
perror("fopen"); /* prints "fopen: No such file..." */
return 1;
}char line[256];
while (fgets(line, sizeof line, fp))
printf("%s", line);
fclose(fp);
| Mode | Meaning |
"r" | Read text. File must exist. |
"w" | Write text. Truncates existing file or creates new. |
"a" | Append text. Creates if missing. Writes go to end. |
"r+" | Read + write. File must exist. |
"rb" / "wb" | Same but binary (no \n translation on Windows). |
Every fopen must be paired with an fclose. Just like malloc/free. Forgetting can corrupt files (data sits in OS buffers and never reaches disk).
Files are how programs persist data between runs. The C model is simple: open a file to get a FILE * handle, use it with f... functions, then close it.
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("Cannot open data.txt");
return 1;
}
/* ... use fp ... */
fclose(fp);fopen returns NULL on failure (file missing, no permission, disk full). Always check.
perror prints your message + the OS error reason ("No such file or directory" etc.).
| Function | What it reads |
fgetc(fp) | One character (returns int, EOF on end) |
fgets(buf, n, fp) | Up to n-1 chars OR until \n — safe |
fscanf(fp, "%d", &x) | Formatted (like scanf) |
fread(buf, size, n, fp) | Raw bytes |
fgets is the safe, beginner-friendly way to read line by line:
char line[256];
while (fgets(line, sizeof line, fp) != NULL) {
/* line ends with '\n' (unless last line had no newline) */
printf("Got: %s", line);
}The loop condition automatically detects end-of-file (fgets returns NULL).
| Function | What it writes |
fputc(c, fp) | One character |
fputs(s, fp) | A string (no newline added) |
fprintf(fp, "...", ...) | Formatted (like printf) |
fwrite(buf, size, n, fp) | Raw bytes |
FILE *out = fopen("scores.txt", "w");
fprintf(out, "Asha %d\n", 95);
fprintf(out, "Ravi %d\n", 88);
fputs("End of file\n", out);
fclose(out);> Beware: "w" mode wipes the file. Use "a" to append.
For non-text data (images, audio, your own structs), use binary mode:
typedef struct { int id; float cgpa; } Student;Student s = {1, 8.9f};
FILE *out = fopen("data.bin", "wb");
fwrite(&s, sizeof s, 1, out); /* write 1 Student */
fclose(out);
Student loaded;
FILE *in = fopen("data.bin", "rb");
fread(&loaded, sizeof loaded, 1, in);
fclose(in);
printf("%d %.2f\n", loaded.id, loaded.cgpa);
fread/fwrite take (pointer, element_size, count, file) and return the count actually read/written.
Two correct ways:
/* fgets returns NULL at EOF */
while (fgets(line, sizeof line, fp)) { ... }/* fscanf returns EOF when nothing matched */
int x;
while (fscanf(fp, "%d", &x) == 1) { ... }
> Wrong way (classic beginner bug): > >
> while (!feof(fp)) { // ❌ reads one extra time after the last value!
> fscanf(fp, "%d", &x);
> printf("%d\n", x);
> }
>
>
> feof becomes true only after a read fails. The loop processes the failed read once. Always check the return value of the read function instead.Files are linear by default, but you can jump around:
fseek(fp, 0, SEEK_END); /* go to end */
long size = ftell(fp); /* current position = file size */
rewind(fp); /* back to start (same as fseek 0, SEEK_SET) */| Whence | Means | |||
SEEK_SET | From beginning | |||
SEEK_CUR | From current position | |||
SEEK_END | From end | The Three Standard StreamsAlready open for you: | Name | Purpose |
stdin | Keyboard input (used by scanf) | |||
stdout | Normal output (used by printf) | |||
stderr | Error messages (unbuffered, separate stream) |
So printf("hi") is really fprintf(stdout, "hi"). And errors should go to stderr:
fprintf(stderr, "Error: %s\n", strerror(errno));FILE *fp = fopen(path, "r");
if (fp == NULL) {
perror(path);
return 1;
}char line[1024];
int line_no = 0;
while (fgets(line, sizeof line, fp)) {
line_no++;
/* process line */
}
if (ferror(fp)) {
fprintf(stderr, "Error reading %s\n", path);
}
fclose(fp);
| Want to | Function |
| Open | fopen(path, mode) |
| Close | fclose(fp) |
| Read line | fgets(buf, n, fp) |
| Read formatted | fscanf(fp, "...", ...) |
| Write formatted | fprintf(fp, "...", ...) |
| Read raw | fread(buf, size, n, fp) |
| Write raw | fwrite(buf, size, n, fp) |
| Jump | fseek(fp, off, whence) |
| Position | ftell(fp) |
| Restart | rewind(fp) |
| Check error | ferror(fp) |
| Why failed? | perror(msg) or strerror(errno) |
You now know enough File I/O to read any text format, write logs, save game state, and process binary blobs.