Chapter 1 — File I/O
Persist data between runs. `fopen` / `fclose`, text mode vs binary mode, and the `f*` family of functions.
What you'll learn
- 1Open and close files cleanly with `fopen`/`fclose`.
- 2Read and write text with `fprintf`, `fscanf`, `fgets`.
- 3Read and write binary with `fread`/`fwrite`.
- 4Use the right open mode (`r`, `w`, `a`, `rb`, etc.).
Core Concepts
1) Open and close
FILE *f = fopen("data.txt", "r");
if (f == NULL) {
perror("fopen");
return 1;
}
// ... use f ...
fclose(f);Always **check for NULL** and always **close**.
2) Open modes
| Mode | Meaning | Position |
|---|---|---|
| `"r"` | read | start |
| `"w"` | write (truncate or create) | start |
| `"a"` | append (create if missing) | end |
| `"r+"` | read + write (must exist) | start |
| `"rb"` / `"wb"` | binary versions | — |
3) Text I/O
fprintf(f, "%d %s\n", 42, "hi");
char buf[100];
fgets(buf, sizeof(buf), f);`fgets` is the safe line reader (knows the buffer size).
4) Binary I/O
int arr[10] = {0};
fwrite(arr, sizeof(int), 10, f);
fread(arr, sizeof(int), 10, f);Binary preserves bytes exactly; not portable across architectures with different endianness.
5) `perror` and `errno`
FILE *f = fopen("nope.txt", "r");
if (!f) { perror("fopen"); } // → fopen: No such file or directoryExamples
Example 1 — `ex01_write_text.c`: write a few lines
FILE *f = fopen("out.txt", "w");
if (!f) return 1;
fprintf(f, "Line 1\n");
fprintf(f, "Line 2\n");
fclose(f);**Output**
(file "out.txt" now contains two lines)Key: `"w"` truncates if the file exists.
Example 2 — `ex02_read_text.c`: read line by line
FILE *f = fopen("out.txt", "r");
char buf[100];
while (fgets(buf, sizeof(buf), f)) {
printf("%s", buf);
}
fclose(f);**Output**
Line 1
Line 2Key: `fgets` returns NULL at EOF — clean loop end.
Example 3 — `ex03_append.c`: append to an existing file
FILE *f = fopen("out.txt", "a");
fprintf(f, "Line 3\n");
fclose(f);**Output**
(file now has 3 lines)Key: `"a"` always writes at the end; creates the file if missing.
Example 4 — `ex04_binary.c`: write/read raw bytes
int data[5] = {1, 2, 3, 4, 5};
FILE *f = fopen("data.bin", "wb");
fwrite(data, sizeof(int), 5, f);
fclose(f);
int read[5];
f = fopen("data.bin", "rb");
fread(read, sizeof(int), 5, f);
fclose(f);
for (int i = 0; i < 5; i++) printf("%d ", read[i]);**Output**
1 2 3 4 5Key: count is **number of elements**, size is **bytes per element**.
Common mistakes
- **Not checking `fopen` return** — silent NULL pointer use later.
- **Forgetting `fclose`** — buffered writes may be lost; OS file handles leak.
- **Mixing text and binary mode** — newline translation on Windows trips you up.
- **Reading into a too-small buffer** — overflow.
Recap
- Always check `fopen` and always `fclose`.
- Pick the right mode (`r`/`w`/`a` + `b` for binary).
- For lines, use `fgets`; for raw blocks, use `fread`/`fwrite`.
Try it
cd src
gcc -std=c11 -Wall -o ex01 ex01_write_text.c && ./ex01
gcc -std=c11 -Wall -o ex02 ex02_read_text.c && ./ex02
gcc -std=c11 -Wall -o ex03 ex03_append.c && ./ex03
gcc -std=c11 -Wall -o ex04 ex04_binary.c && ./ex04💻 Examples
Compilable, runnable examples — see the output yourself.
#include <stdio.h>
int main(void) {
FILE *fp = fopen("output.txt", "w");
if (!fp) { perror("output.txt"); return 1; }
fprintf(fp, "Name,Score\n");
fprintf(fp, "%s,%d\n", "Kim", 90);
fprintf(fp, "%s,%d\n", "Lee", 85);
fprintf(fp, "%s,%d\n", "Park", 78);
fclose(fp);
printf("output.txt \n");
return 0;
}
(file "out.txt" now contains two lines)#include <stdio.h>
int main(void) {
FILE *fp = fopen("output.txt", "r");
if (!fp) {
perror("output.txt");
printf("ex01_write_text .\n");
return 1;
}
char line[256];
int ln = 1;
while (fgets(line, sizeof(line), fp) != NULL) {
printf("%2d: %s", ln++, line);
}
fclose(fp);
return 0;
}
Line 1
Line 2#include <stdio.h>
#include <time.h>
int main(void) {
FILE *fp = fopen("log.txt", "a");
if (!fp) { perror("log.txt"); return 1; }
time_t t = time(NULL);
fprintf(fp, "[%ld] \n", (long)t);
fclose(fp);
printf("log.txt \n");
return 0;
}
(file now has 3 lines)#include <stdio.h>
typedef struct {
int id;
char name[32];
double score;
} Record;
int main(void) {
Record out[] = {
{1, "Kim", 90.5},
{2, "Lee", 85.0},
{3, "Park", 77.5},
};
int n = (int)(sizeof(out) / sizeof(out[0]));
FILE *fp = fopen("data.bin", "wb");
if (!fp) { perror("data.bin"); return 1; }
fwrite(out, sizeof(Record), n, fp);
fclose(fp);
Record in[3];
fp = fopen("data.bin", "rb");
if (!fp) { perror("data.bin"); return 1; }
size_t r = fread(in, sizeof(Record), n, fp);
fclose(fp);
printf(" : %zu\n", r);
for (size_t i = 0; i < r; i++) {
printf("%d %-6s %.2f\n", in[i].id, in[i].name, in[i].score);
}
return 0;
}
1 2 3 4 5📝 Exercises
Try them yourself first, then open the solution to compare.
Problem 1 (hw01.c)
Goal: Write 5 lines to `out.txt`, then read back and count words.
- Filename: hw01.c
▶Toggle solution
#include <stdio.h>
int main(void) {
FILE *fp = fopen("numbers.txt", "w");
if (!fp) { perror("numbers.txt"); return 1; }
printf("Enter 5 ints: ");
for (int i = 0; i < 5; i++) {
int n;
scanf("%d", &n);
fprintf(fp, "%d\n", n);
}
fclose(fp);
printf("numbers.txt saved\n");
return 0;
}
Problem 2 (hw02.c)
Goal: Read a text file and print each line prefixed with its line number.
- Filename: hw02.c
▶Toggle solution
#include <stdio.h>
int main(void) {
FILE *fp = fopen("numbers.txt", "r");
if (!fp) { perror("numbers.txt"); return 1; }
int n, sum = 0, count = 0;
while (fscanf(fp, "%d", &n) == 1) {
sum += n;
count++;
}
fclose(fp);
if (count == 0) { printf("No data.\n"); return 0; }
printf("sum: %d\n", sum);
printf("mean: %.2f\n", (double)sum / count);
return 0;
}
Problem 3 (hw03.c)
Goal: Write a tiny `cp` — copy `src.txt` to `dst.txt`.
- Filename: hw03.c
▶Toggle solution
#include <stdio.h>
int main(void) {
int count = 0;
FILE *fp = fopen("count.bin", "rb");
if (fp) {
fread(&count, sizeof(int), 1, fp);
fclose(fp);
}
count++;
fp = fopen("count.bin", "wb");
if (!fp) { perror("count.bin"); return 1; }
fwrite(&count, sizeof(int), 1, fp);
fclose(fp);
printf("run count: %d\n", count);
return 0;
}
All lecture materials and example code are openly available on GitHub.
View on GitHub ↗