Use streams and check every operation that can fail
FILE is the standard-library stream type. fopen returns a pointer to a stream on success and NULL on failure. Files can fail to open because of missing directories, permissions or resource limits; writing can fail later even after opening succeeds.
Write, close and read a small text file
#include
int main(void) {
FILE *file = fopen("sample.txt", "w");
if (file == NULL) {
perror("open for writing");
return 1;
}
if (fprintf(file, "Asha 82\n") < 0) {
fclose(file);
return 1;
}
if (fclose(file) == EOF) {
perror("close after writing");
return 1;
}
file = fopen("sample.txt", "r");
if (file == NULL) {
perror("open for reading");
return 1;
}
char line[80];
while (fgets(line, sizeof line, file) != NULL) {
fputs(line, stdout);
}
int failed = ferror(file);
if (fclose(file) == EOF) failed = 1;
return failed ? 1 : 0;
} In a writable working directory, the output is Asha 82. This example intentionally creates or truncates sample.txt. Do not point it at an existing file you need to preserve. Some online compilers use temporary files that disappear after execution.
Choose the correct mode
r opens an existing file for reading. w creates a file or truncates an existing one. a appends output, creating the file if necessary. Binary modes add b, such as rb or wb, when exact byte treatment is required. Update streams such as r+ need the standard's positioning/flush rules when switching between reading and writing.
Read based on the operation, not feof first
The loop while (!feof(file)) is a common mistake: end-of-file is reported only after an operation tries to read beyond available input. Instead, make the read operation itself the loop condition. After it stops, use ferror to distinguish a read error from normal end-of-file.
When reading a single character, store fgetc's result in int so EOF remains distinguishable from character values. fscanf can parse formatted tokens, but check its assignment count and bound string conversions. fgets followed by explicit parsing is often clearer for records.
Exercises
- Change the example to append a second record with mode a, then read both lines.
- Try opening a nonexistent file with r and confirm the failure path is handled.
- Count lines using fgets. Account for a final line that may not end in a newline.
- Explain why directly dumping a struct to a binary file does not create a portable interchange format: padding, byte order and representation can differ.