File Handling in C: fopen, fclose, fprintf, fscanf, fread, fwrite 💾
File Handling in C: fopen, fclose, fprintf, fscanf, fread, fwrite is a core C concept covering master C File Handling. Learn the FILE pointer, fopen modes, text I/O (fprintf/fscanf) vs binary I/O (fread/fwrite), error checks, and EOF handling. This topic is essential for academic learning, board exam preparation, and developing optimized real-world code.
Mentor's Note: Think of your computer's RAM as a whiteboard. You write numbers on it, but the moment you turn off the computer (or the program terminates), the whiteboard is wiped clean. If you want your data to survive, you must write it down in a notebook (Hard Drive/SSD). In C, file handling functions are the pens that allow you to read and write to this permanent notebook! 💡
📚 Educational Content: File Handling is a critical module in Board Exams (GSEB/CBSE Class 12) and university exams (BCA Sem 1 / BE). Theory questions focus on "Text vs Binary Files", "File Opening Modes", and debugging code blocks that fail to handle file-not-found exceptions.
:::info What You'll Learn By the end of this tutorial, you'll know:
- What a
FILE*pointer is and how streams operate. - The various file opening modes (
r,w,a,rb,wb, etc.). - How to write and read text using
fprintf()andfscanf(). - How to write and read structures directly using binary
fwrite()andfread(). - The common bugs involving
feof()and how to check for end-of-file safely. - A fully functional save/load implementation for a student database. :::
🌟 The Scenario: The Vanishing Database
Imagine you write a beautiful Student Management Program:
- You input details for 50 students, calculate their grades, and view their cards.
- The Logic: All this data lives in RAM. When you close the program or shut down the PC:
RAM power goes off ➔ Data disappears❌ - The Result: You lose all student records! To prevent this, you save the student arrays into a binary file
students.datbefore closing. The next time the program starts, it loads the data back into memory. ✅
📖 Concept Explanation
File handling in C centers around the standard library stream pointer, FILE *. This pointer tracks the file location on the disk, the read/write cursor position, and error status flags.
1. File Opening (fopen) and Closing (fclose)
We open files using fopen(), which returns a FILE * handle.
FILE *fp = fopen("data.txt", "r");
So what? If the file doesn't exist or is locked, fopen returns NULL. You must check for this, or dereferencing it will crash your program.
When finished, always release the file handle:
fclose(fp);
So what? Closing a file flushes any pending writes from RAM buffers onto the physical disk and frees the operating system file locks.
2. File Opening Modes
| Mode | Type | Action if File Exists | Action if File Does Not Exist |
|---|---|---|---|
"r" | Text (Read) | Opens for reading. | Returns NULL. |
"w" | Text (Write) | Overwrites / truncates content. | Creates new file. |
"a" | Text (Append) | Appends data at the end. | Creates new file. |
"rb" | Binary (Read) | Opens binary file for reading. | Returns NULL. |
"wb" | Binary (Write) | Overwrites binary file. | Creates new file. |
3. Text vs Binary File I/O
- Text Mode (
fprintf/fscanf): Data is written as human-readable ASCII text characters. So what? Newline characters (\n) are automatically converted to match the operating system's standard (e.g.\r\non Windows). - Binary Mode (
fwrite/fread): Data is written as raw bytes from memory. So what? It is faster, uses less disk space, and allows saving complex data types like structs in a single call.
// Binary Write Syntax
fwrite(&my_struct, sizeof(my_struct), 1, fp);
4. The feof() Trap
A common bug is writing while(!feof(fp)) to read files.
So what? The end-of-file flag is only set after a read operation has failed. If you use while(!feof(fp)), the loop will run one extra time, processing duplicate or garbage values at the end of the file. The correct way is checking the return value of your read functions (like fscanf or fread).
🧠 Step-by-Step Logic
Let's design a system to save and load student data:
- Start 🏁
- Define a
Studentstructure. - Open a file
students.binin binary write (wb) mode. - Check if file pointer is
NULL. If yes, terminate. - Write the records using
fwrite(). Close file. - Open the file in binary read (
rb) mode. - Read records using
fread()in a loop until read count is less than 1. - Display the loaded records. Close file.
- End 🏁
💻 C Implementation (C99)
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int roll_no;
char name[50];
float gpa;
} Student;
// Function declarations
void saveRecords(const char *filename, const Student *list, int count);
void loadRecords(const char *filename);
int main() {
// 🛒 Scenario: Persistent Student Database
// 🚀 Action: Write struct array to file and read it back
const char *db_file = "students.bin";
Student school_list[3] = {
{101, "Aarav Mehta", 9.2},
{102, "Diya Patel", 8.8},
{103, "Kabir Shah", 9.5}
};
printf("--- Saving records to file ---\n");
saveRecords(db_file, school_list, 3);
printf("\n--- Loading and displaying records ---\n");
loadRecords(db_file);
return 0;
}
// Function to save records using fwrite (Binary Write)
void saveRecords(const char *filename, const Student *list, int count) {
FILE *fp = fopen(filename, "wb"); // Open in binary write
if (fp == NULL) {
perror("Error opening file for writing");
exit(1);
}
// Write entire array of structs in a single instruction
size_t written = fwrite(list, sizeof(Student), count, fp);
printf("Successfully wrote %lu student records.\n", written);
fclose(fp); // Close to flush buffer
}
// Function to read records using fread (Binary Read)
void loadRecords(const char *filename) {
FILE *fp = fopen(filename, "rb"); // Open in binary read
if (fp == NULL) {
perror("Error opening file for reading");
exit(1);
}
Student temp;
// Read one structure at a time until end-of-file is hit
// fread returns the number of objects successfully read
while (fread(&temp, sizeof(Student), 1, fp) == 1) {
printf("Roll: %d | Name: %-15s | GPA: %.2f\n",
temp.roll_no, temp.name, temp.gpa);
}
fclose(fp);
}
// Output:
// --- Saving records to file ---
// Successfully wrote 3 student records.
//
// --- Loading and displaying records ---
// Roll: 101 | Name: Aarav Mehta | GPA: 9.20
// Roll: 102 | Name: Diya Patel | GPA: 8.80
// Roll: 103 | Name: Kabir Shah | GPA: 9.50
📊 Sample Dry Run
Let's trace the file reading loop inside loadRecords():
| Loop Iteration | Position in File (Bytes) | fread() Return Value | Destination Struct temp | Action taken |
|---|---|---|---|---|
| Start | 0 | - | - | File opened successfully |
| 1 | 0 ➔ 56 | 1 | {101, "Aarav Mehta", 9.2} | Printed successfully ✅ |
| 2 | 56 ➔ 112 | 1 | {102, "Diya Patel", 8.8} | Printed successfully ✅ |
| 3 | 112 ➔ 168 | 1 | {103, "Kabir Shah", 9.5} | Printed successfully ✅ |
| 4 | 168 | 0 (EOF hit) | - | Loop terminates safely |
📉 Complexity Analysis
Time Complexity ⏱️
- Binary I/O Operations: $O(N)$ where $N$ is the number of records read or written.
- Disk Latency: Reading from disk is thousands of times slower than reading from RAM. Minimizing file open/close counts is recommended.
Space Complexity 💾
- RAM footprint: $O(1)$ auxiliary space during read loops (uses a single buffer struct variable).
- Disk footprint:
O(N * sizeof(struct))bytes.
🎯 Practice Problems
Easy Level 🟢
- Write a program to read a text file
notes.txtcharacter by character usingfgetc()and display the output on the terminal.
Medium Level 🟡
- Write a program to copy the contents of one text file to another, while converting all lowercase letters to uppercase letters in the process.
Hard Level 🔴
- Create a library management application. Define a structure
Book(BookID, Title, Status). Write functions that allow:- Adding a new book record (append).
- Displaying all books (read).
- Updating the Status (Issued/Available) of a book matching a specific BookID (requires reading, modifying, and rewriting).
❓ Frequently Asked Questions
Q: What is the difference between text mode ("w") and binary mode ("wb")?
In text mode, system-specific conversions happen (e.g. \n translates to \r\n on Windows). In binary mode, memory contents are written directly to disk bit-by-bit without translation, ensuring compatibility.
Q: Why should we never check while(!feof(fp)) directly?
feof() returns true only after a read operation fails. In while(!feof(fp)), the loop runs even when the file pointer is at EOF. The final read operation fails, but the loop logic executes one more time using old buffer data.
Q: What is the purpose of fflush()?
fflush(FILE *stream) forces the system to write any buffered output data in memory to the physical disk immediately, without waiting for the file to close.
📚 Best Practices & Common Mistakes
✅ Best Practices
- Always Check NULL: Always verify if
fopenreturnedNULLbefore reading or writing. Failing to check causes immediate crashes. - Always Close Streams: Close all files using
fcloseto prevent memory leaks and file corruption issues. - Check Return Values: Inspect
freadandfwritereturn counts to confirm all operations succeeded.
❌ Common Mistakes ⚠️
- Invalid Path Slash: Using single backslashes in paths on Windows (e.g.
fopen("c:\temp\data.txt", "r")). You must write double backslashes\\(e.g.,"c:\\temp\\data.txt") or use forward slashes/. - Wrong Mode Overwrites: Opening a file in
"w"mode when you intend to add records."w"truncates the file immediately, deleting all existing records. Use"a"or"r+"instead.
✅ Summary
In this tutorial, you've learned:
- ✅ Files allow storing data permanently on storage devices.
- ✅ The
FILE *pointer tracks file information and stream properties. - ✅ Opening files requires checking for
NULLto prevent program crashes. - ✅ Binary operations (
fread/fwrite) are faster and handle structure variables seamlessly. - ✅ Loop checks should inspect read execution success counts instead of using
feof().
💡 Interview Tips & Board Focus 👔
- Text vs Binary: Frequently asked in practical viva examinations. Focus on the raw byte nature of binary files.
- feof() Bug Explanation: Explain why checking return value is safer. This shows standard coding habits.
📚 Further Reading
Continue your learning path:
- ← Previous: Preprocessor Macros — review preprocessor directives
- Next: C Programming Track Overview — continue exploring standard structures