1. The Core Idea: Why Do We Need Files?
All the variables and data we've used so far (like in arrays or structs) are stored in RAM (Random Access Memory). RAM is volatile, which means as soon as your program ends or the power goes out, all that data is *erased forever*.
File Handling is the solution. It allows your C program to read and write data to a file on the hard drive (or SSD). The hard drive is non-volatile (permanent storage). This lets you save your program's state, store user data, create logs, and much more.
Analogy: Whiteboard vs. Notebook
RAM (Variables): Think of RAM as a whiteboard. It's fast to write on and read from, but when you're done with the meeting (your program ends), it gets erased.
Files (Hard Drive): Think of a file as a notebook. To use it, you have to find it, open it to the right page, and then write in it with a pen. It's slower, but when you close the notebook, the data is saved permanently. You can come back tomorrow, open it, and all your notes are still there.
2. The FILE Pointer
You *never* access
a file directly. Instead, C manages a connection to the file for you using a
special structure called FILE (defined in <stdio.h>).
To work with a file, you must create a pointer to this structure. This pointer is like your "bookmark" or "handle" for the file. It keeps track of where you are in the file, whether you're reading or writing, etc.
It is always declared like this:
// 'fptr' is a pointer that will hold the connection to our file
FILE* fptr;3. The 3-Step Process for All File I/O
Working with files *always* follows these three steps:
1. Open the
file using fopen(). This
"connects" your FILE* pointer
to the physical file on the hard drive.
2. Read or Write to
the file using functions like fprintf(), fscanf(), fputc(), fgetc(), etc.
3. Close the
file using fclose(). This
"disconnects" from the file and, most importantly, saves
your changes.
4. Step 1:
Opening a File with fopen()
This function
tries to open a file and returns a FILE* pointer to it. If it fails (e.g., file doesn't exist in
"read" mode), it returns NULL.
Syntax: fptr = fopen("filename",
"mode");
·
"filename": The
name of the file (e.g., "notes.txt").
·
"mode": A
string that tells C *how* you want to open the file.
Common File Modes
|
Mode |
Meaning |
If File Exists? |
If File Doesn't Exist? |
|
|
Read |
Opens for reading. (Cursor at start) |
Fails (returns |
|
|
Write |
ERASES (Truncates) the file! Then opens for writing. |
Creates a new, empty file. |
|
|
Append |
Opens for writing. (Cursor at end) |
Creates a new, empty file. |
|
|
Read + Write |
Opens for reading and writing. (Cursor at start) |
Fails (returns |
|
|
Read + Write |
ERASES (Truncates) the file! |
Creates a new, empty file. |
|
|
Read + Append |
Opens for reading and writing. (Cursor at end for writing) |
Creates a new, empty file. |
Critical: Always Check for NULL
After calling fopen(), you must check
if the pointer is NULL. This is the most common source of bugs in file handling. It
prevents your program from crashing if the file can't be opened.
FILE* fptr;fptr = fopen("data.txt", "r"); // Try to open for reading
if (fptr == NULL) {
printf("Error: Could not open file data.txt!\n");
return 1; // Exit the program
} // If we are here, the file opened successfully!
5. Step 2: Reading & Writing (Text Files)
These functions
work just like printf and scanf, but they take a FILE* pointer as their first argument.
·
fprintf(fptr,
"format string", ...);: Writes formatted data *to* a
file.
·
fscanf(fptr,
"format string", ...);: Reads formatted data *from*
a file.
Other common functions:
·
fputc(char
c, FILE *fptr);: Writes a single character c to
the file.
·
fgetc(FILE
*fptr);: Reads a single character from the file. Returns EOF at the end.
·
fputs(const
char *str, FILE *fptr);: Writes a string str to the file.
·
fgets(char
*str, int n, FILE *fptr);: Reads a line (up to n-1 chars) from the file into str.
6. Step 3:
Closing a File with fclose()
This function "disconnects" from the file and, most importantly, **flushes the output buffer**. This means any data you "wrote" (which might still be in RAM) is permanently saved to the hard drive. Forgetting to close a file is a common bug that results in empty or incomplete files.
fclose(fptr); // Save changes and close the connection
fptr = NULL; // Good practice to prevent accidental use
7. Complete Example 1: Writing and Reading a Text File
This program will (A) create a file named `poem.txt` and write to it, then (B) close it, re-open it, and read the data back.
#include <stdio.h>
#include <stdlib.h> // For exit()
int main() {
FILE* fptr;char lineBuffer[100]; // To store a line we read
// === PART A: WRITE TO THE FILE ===
// 1. Open in "write" mode ("w" will erase the file if it exists)
fptr = fopen("poem.txt", "w");
// 2. Check for failure
if (fptr == NULL) {
printf("Error opening file for writing!\n");
exit(1);
} // 3. Write data using fprintf (works just like printf)
fprintf(fptr, "Roses are red,\n");
fprintf(fptr, "Violets are blue,\n");
fprintf(fptr, "File handling in C\n");
fprintf(fptr, "Is fun to do!\n");
// 4. Close the file (this saves the data)
fclose(fptr);
printf("Wrote poem to poem.txt!\n\n");
// === PART B: READ FROM THE FILE ===
// 1. Open in "read" mode
fptr = fopen("poem.txt", "r");
// 2. Check for failure
if (fptr == NULL) {
printf("Error opening file for reading!\n");
exit(1);
} printf("--- Reading from poem.txt ---\n");
// 3. Read data line-by-line using fgets
// fgets reads until it hits a newline, (n-1) chars, or EOF
// It returns NULL when it reaches the end of the file.
while ( fgets(lineBuffer, sizeof(lineBuffer), fptr) != NULL ) {
// Print the line we just read
printf("%s", lineBuffer);
} // 4. Close the file
fclose(fptr);
return 0;
}8. Extra
Content: Binary Files (fread & fwrite)
The methods above
are for text files (human-readable). What if you
want to save a struct or an array of ints? Writing them as text is inefficient.
Instead, we use binary mode. This writes the raw, in-memory bytes of your variable directly to the file. It's faster and takes up less space, but the file is *not* human-readable in Notepad.
·
Modes: You add a "b" to the mode
string: "wb" (write
binary), "rb" (read
binary).
·
Functions: You use fwrite and fread.
fwrite() Syntax:
fwrite(&ptr_to_data, size_of_one_element, num_elements,
fptr);
Example: fwrite(&myStudent, sizeof(Student), 1,
fptr); // Writes
1 Student struct
fread() Syntax:
fread(&ptr_to_store_data, size_of_one_element,
num_elements, fptr);
Example: fread(&myStudent, sizeof(Student), 1,
fptr); // Reads 1
Student struct
9. Complete
Example 2: Saving a struct (Binary)
This program ties
everything together from Unit 4. It defines a Student struct, saves one instance to a
binary file `student.dat`, and then reads it back.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[50];
int roll;
float gpa;
} Student; int main() {
FILE* fptr;Student s_write = {"Alice", 101, 3.8}; // The struct we will write
Student s_read; // An empty struct to read into
// === PART A: WRITE STRUCT TO BINARY FILE ===
// 1. Open in "write binary" mode
fptr = fopen("student.dat", "wb");
if (fptr == NULL) {
printf("Error opening student.dat for writing!\n");
exit(1);
} // 2. Write the struct
// Address of data: &s_write // Size of 1 element: sizeof(Student) // Number of elements: 1 // File pointer: fptrfwrite(&s_write, sizeof(Student), 1, fptr);
// 3. Close the filefclose(fptr);
printf("Wrote student struct to student.dat\n\n");
// === PART B: READ STRUCT FROM BINARY FILE === // 1. Open in "read binary" modefptr = fopen("student.dat", "rb");
if (fptr == NULL) {
printf("Error opening student.dat for reading!\n");
exit(1);
} // 2. Read the struct from the file into our empty variablefread(&s_read, sizeof(Student), 1, fptr);
// 3. Close the filefclose(fptr);
// 4. Print the data we just read to verify it workedprintf("--- Reading from student.dat ---\n");
printf("Name: %s\n", s_read.name);
printf("Roll: %d\n", s_read.roll);
printf("GPA: %.2f\n", s_read.gpa);
return 0;
}