Notifications

No notifications

/Phase 3

File I/O (FILE *, fopen, fread)

Read & Write Files Like a Pro 📂

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);

The 6 Modes You'll Use

ModeMeaning
"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).

The Golden Rule

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

On this page

Detailed Theory

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.

Open & Close

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

Reading Text Files

FunctionWhat it reads
fgetc(fp)One character (returns int, EOF on end)
fgets(buf, n, fp)Up to n-1 chars OR until \nsafe
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).

Writing Text Files

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

Binary Files (fread / fwrite)

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.

Detecting End of File

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.

Random Access — fseek / ftell / rewind

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) */

WhenceMeans
SEEK_SETFrom beginning
SEEK_CURFrom current position
SEEK_ENDFrom end

The Three Standard Streams

Already open for you:

NamePurpose
stdinKeyboard input (used by scanf)
stdoutNormal output (used by printf)
stderrError 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));

A Robust File-Read Skeleton

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);

File I/O Cheat-Sheet

Want toFunction
Openfopen(path, mode)
Closefclose(fp)
Read linefgets(buf, n, fp)
Read formattedfscanf(fp, "...", ...)
Write formattedfprintf(fp, "...", ...)
Read rawfread(buf, size, n, fp)
Write rawfwrite(buf, size, n, fp)
Jumpfseek(fp, off, whence)
Positionftell(fp)
Restartrewind(fp)
Check errorferror(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.