← Back to C series
🔥
Deep
fopen · fread/fwrite · text/binary

Chapter 1 — File I/O

Persist data between runs. `fopen` / `fclose`, text mode vs binary mode, and the `f*` family of functions.

fopenfreadfwrite
Duration
1-2 hours
Level
📊 Advanced
Prerequisite
🎯 Advanced 3
Outcome
Persist data to disk

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

c
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

ModeMeaningPosition
`"r"`readstart
`"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

c
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

c
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`

c
FILE *f = fopen("nope.txt", "r");
if (!f) { perror("fopen"); }   // → fopen: No such file or directory

Examples

Example 1 — `ex01_write_text.c`: write a few lines

c
FILE *f = fopen("out.txt", "w");
if (!f) return 1;
fprintf(f, "Line 1\n");
fprintf(f, "Line 2\n");
fclose(f);

**Output**

text
(file "out.txt" now contains two lines)

Key: `"w"` truncates if the file exists.

Example 2 — `ex02_read_text.c`: read line by line

c
FILE *f = fopen("out.txt", "r");
char buf[100];
while (fgets(buf, sizeof(buf), f)) {
    printf("%s", buf);
}
fclose(f);

**Output**

text
Line 1
Line 2

Key: `fgets` returns NULL at EOF — clean loop end.

Example 3 — `ex03_append.c`: append to an existing file

c
FILE *f = fopen("out.txt", "a");
fprintf(f, "Line 3\n");
fclose(f);

**Output**

text
(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

c
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**

text
1 2 3 4 5

Key: count is **number of elements**, size is **bytes per element**.

Common mistakes

  1. **Not checking `fopen` return** — silent NULL pointer use later.
  2. **Forgetting `fclose`** — buffered writes may be lost; OS file handles leak.
  3. **Mixing text and binary mode** — newline translation on Windows trips you up.
  4. **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

bash
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.

ex01_write_text.cwrite a few lines
CODE
#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;
}
▶ Output
(file "out.txt" now contains two lines)
ex02_read_text.cread line by line
CODE
#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;
}
▶ Output
Line 1
Line 2
ex03_append.cappend to an existing file
CODE
#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;
}
▶ Output
(file now has 3 lines)
ex04_binary.cwrite/read raw bytes
CODE
#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;
}
▶ Output
1 2 3 4 5

📝 Exercises

Try them yourself first, then open the solution to compare.

Exercise 1

Problem 1 (hw01.c)

Goal: Write 5 lines to `out.txt`, then read back and count words.

Requirements
  • Filename: hw01.c
Toggle solution
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;
}
Exercise 2

Problem 2 (hw02.c)

Goal: Read a text file and print each line prefixed with its line number.

Requirements
  • Filename: hw02.c
Toggle solution
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;
}
Exercise 3

Problem 3 (hw03.c)

Goal: Write a tiny `cp` — copy `src.txt` to `dst.txt`.

Requirements
  • Filename: hw03.c
Toggle solution
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;
}
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗