File I/O
fopen/fclose, reading/writing text and binary files, error handling with errno
Introduction
In this lesson, you'll learn about file i/o in C. Coming from Python, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In Python, you're familiar with fopen/fclose, reading/writing text and binary files, error handling with errno.
C has its own approach to fopen/fclose, reading/writing text and binary files, error handling with errno, which we'll explore step by step.
The C Way
Let's see how C handles this concept. Here's a typical example:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(void) {
// Open file — always check for NULL
FILE *f = fopen("data.txt", "r");
if (f == NULL) {
fprintf(stderr, "Error opening file: %s\n", strerror(errno));
return 1;
}
// Read line by line
char line[256];
while (fgets(line, sizeof(line), f) != NULL) {
printf("%s", line); // fgets includes '\n'
}
fclose(f); // MUST close!
// Write text
FILE *out = fopen("out.txt", "w"); // "a" for append
if (out == NULL) { perror("fopen"); return 1; }
fprintf(out, "Hello, %s!\n", "World");
fputs("another line\n", out);
fclose(out);
// Binary read/write
FILE *img = fopen("img.png", "rb");
FILE *dst = fopen("copy.png", "wb");
char buf[4096];
size_t n;
while ((n = fread(buf, 1, sizeof(buf), img)) > 0) {
fwrite(buf, 1, n, dst);
}
fclose(img);
fclose(dst);
return 0;
}Comparing to Python
Here's how you might have written similar code in Python:
# Text read
with open("data.txt", "r", encoding="utf-8") as f:
content = f.read() # entire file
# lines = f.readlines() # list of lines
# for line in f: ...
# Text write
with open("out.txt", "w") as f:
f.write("Hello!\n")
# Append
with open("log.txt", "a") as f:
f.write("entry\n")
# Binary read/write
with open("img.png", "rb") as f:
data = f.read()
with open("copy.png", "wb") as f:
f.write(data)
# JSON
import json
with open("data.json") as f:
obj = json.load(f)
# Check existence
from pathlib import Path
if Path("file.txt").exists():
...You may be used to different syntax or behavior.
fopen returns NULL on failure; always check — no automatic exception
You may be used to different syntax or behavior.
fgets reads one line at a time (including newline); read() in Python reads all
You may be used to different syntax or behavior.
MUST call fclose() explicitly — no context manager auto-close in C
You may be used to different syntax or behavior.
fprintf for formatted write (like Python f-strings); fputs for plain strings
You may be used to different syntax or behavior.
Binary mode: 'rb'/'wb' — critical on Windows (text mode translates \r\n)
Step-by-Step Breakdown
1. Opening and Closing Files
fopen returns a FILE* or NULL on error. Always check for NULL and always call fclose(). No automatic cleanup in C.
with open("file.txt") as f:
# auto-closesFILE *f = fopen("file.txt", "r");
if (f == NULL) { perror("fopen"); return 1; }
// ... use f ...
fclose(f); // MUST call or file handle leaks2. Reading Lines
fgets reads up to n-1 chars including the newline. EOF is detected by fgets returning NULL.
for line in f:
process(line)char buf[256];
while (fgets(buf, sizeof(buf), f) != NULL) {
// buf includes '\n' at end
buf[strcspn(buf, "\n")] = '\0'; // strip newline
process(buf);
}3. Formatted Writing
fprintf writes to a file like printf to stdout. Use %s, %d, %f format specifiers.
f.write(f"name={name}, age={age}\n")fprintf(f, "name=%s, age=%d\n", name, age);4. Binary Files
Use 'rb'/'wb' for binary files. fread/fwrite operate on raw bytes, perfect for copying or processing non-text files.
with open("img.png", "rb") as f:
data = f.read()FILE *f = fopen("img.png", "rb");
char buf[4096]; size_t n;
while ((n = fread(buf, 1, sizeof(buf), f)) > 0) {
process(buf, n);
}
fclose(f);Common Mistakes
When coming from Python, developers often make these mistakes:
- fopen returns NULL on failure; always check — no automatic exception
- fgets reads one line at a time (including newline); read() in Python reads all
- MUST call fclose() explicitly — no context manager auto-close in C
Key Takeaways
- fopen returns NULL on failure — always check; fclose must be called explicitly
- fgets reads one line (including \n); fread/fwrite for binary data
- fprintf for formatted output to file; fputs for plain strings
- Binary mode 'rb'/'wb' essential on Windows to avoid \r\n translation