C File Handling - Complete Guide
Master C file handling including file operations, file modes, file functions with detailed syntax, practical examples, and programming best practices.
File Operations
Create, Read, Write, Update
File Modes
Text & Binary Modes
File Functions
Complete Reference
Introduction to File Handling in C
File handling in C enables programs to store data permanently on disk and retrieve it when needed. It's essential for creating applications that need to persist data between program executions, such as databases, configuration files, and data logs.
Why File Handling?
- Data Persistence: Store data permanently on disk
- Large Data Handling: Process data larger than available memory
- Data Sharing: Share data between different programs
- Configuration Storage: Store program settings and preferences
- Backup & Recovery: Create backups and restore data
File Types in C
- Text Files: Human-readable, store characters
- Binary Files: Machine-readable, store raw bytes
- Sequential Files: Processed in sequence
- Random Access Files: Access any position directly
Important File Concepts
File handling involves: FILE pointer (stream pointer), Opening mode (read/write/append), File position indicator (current read/write position), and Buffer (temporary storage). Always close files after operations to prevent data loss.
File Opening Modes
File opening modes determine how a file will be accessed. Each mode has specific characteristics for text and binary files.
| Mode | Description | File Exists | File Doesn't Exist |
|---|---|---|---|
| Text File Modes | |||
|
"r"
|
Read mode. Opens file for reading. | Opens successfully | Returns NULL |
|
"w"
|
Write mode. Creates new file or truncates existing. | Truncates to zero length | Creates new file |
|
"a"
|
Append mode. Opens file for writing at end. | Opens, writes at end | Creates new file |
|
"r+"
|
Read/Write mode. Opens file for both. | Opens successfully | Returns NULL |
|
"w+"
|
Write/Read mode. Creates/truncates file. | Truncates to zero length | Creates new file |
|
"a+"
|
Append/Read mode. Opens for reading and appending. | Opens, writes at end | Creates new file |
| Binary File Modes | |||
|
"rb"
|
Binary read mode. | Opens successfully | Returns NULL |
|
"wb"
|
Binary write mode. | Truncates to zero length | Creates new file |
|
"ab"
|
Binary append mode. | Opens, writes at end | Creates new file |
|
"rb+"
|
Binary read/write mode. | Opens successfully | Returns NULL |
|
"wb+"
|
Binary write/read mode. | Truncates to zero length | Creates new file |
|
"ab+"
|
Binary append/read mode. | Opens, writes at end | Creates new file |
C File Functions Reference Table
Complete reference of all file handling functions in C with their syntax, purpose, and return values.
| Function | Syntax | Purpose | Return Value |
|---|---|---|---|
| fopen() |
FILE *fopen(const char *filename, const char *mode);
|
Opens a file and returns a FILE pointer | FILE* on success, NULL on failure |
| fclose() |
int fclose(FILE *stream);
|
Closes an opened file | 0 on success, EOF on error |
| fgetc() |
int fgetc(FILE *stream);
|
Reads a character from file | Character read or EOF |
| fputc() |
int fputc(int char, FILE *stream);
|
Writes a character to file | Character written or EOF |
| fgets() |
char *fgets(char *str, int n, FILE *stream);
|
Reads a string from file | str on success, NULL on failure |
| fputs() |
int fputs(const char *str, FILE *stream);
|
Writes a string to file | Non-negative on success, EOF on error |
| fscanf() |
int fscanf(FILE *stream, const char *format, ...);
|
Reads formatted input from file | Number of items read |
| fprintf() |
int fprintf(FILE *stream, const char *format, ...);
|
Writes formatted output to file | Number of characters written |
| fread() |
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
|
Reads binary data from file | Number of items read |
| fwrite() |
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
|
Writes binary data to file | Number of items written |
| fseek() |
int fseek(FILE *stream, long offset, int whence);
|
Moves file position indicator | 0 on success, non-zero on error |
| ftell() |
long ftell(FILE *stream);
|
Returns current file position | Current position or -1L on error |
| rewind() |
void rewind(FILE *stream);
|
Sets file position to beginning | None (void) |
| feof() |
int feof(FILE *stream);
|
Checks end-of-file indicator | Non-zero if EOF, 0 otherwise |
| ferror() |
int ferror(FILE *stream);
|
Checks error indicator | Non-zero if error, 0 otherwise |
| clearerr() |
void clearerr(FILE *stream);
|
Clears error and EOF indicators | None (void) |
| remove() |
int remove(const char *filename);
|
Deletes a file | 0 on success, non-zero on error |
| rename() |
int rename(const char *oldname, const char *newname);
|
Renames a file | 0 on success, non-zero on error |
fopen() for NULL before proceeding.
Always close files with fclose() after operations to free resources and ensure data is written to disk.
Text File Operations
Text files store data in human-readable format. Each line ends with a newline character ('\n').
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *file;
char ch;
char text[100];
// Writing to a text file
file = fopen("example.txt", "w");
if(file == NULL) {
printf("Error opening file for writing!\n");
return 1;
}
fprintf(file, "Hello, World!\n");
fprintf(file, "This is a text file.\n");
fprintf(file, "Line 3: %d + %d = %d\n", 5, 3, 5+3);
fclose(file);
printf("Data written to file successfully.\n\n");
// Reading from a text file - character by character
file = fopen("example.txt", "r");
if(file == NULL) {
printf("Error opening file for reading!\n");
return 1;
}
printf("Reading file character by character:\n");
while((ch = fgetc(file)) != EOF) {
printf("%c", ch);
}
fclose(file);
// Reading from a text file - line by line
file = fopen("example.txt", "r");
printf("\n\nReading file line by line:\n");
while(fgets(text, sizeof(text), file) != NULL) {
printf("Line: %s", text);
}
fclose(file);
// Appending to a text file
file = fopen("example.txt", "a");
fprintf(file, "This line was appended.\n");
fprintf(file, "Appended line 2.\n");
fclose(file);
printf("\nData appended successfully.\n");
return 0;
}
Data written to file successfully.
Reading file character by character:
Hello, World!
This is a text file.
Line 3: 5 + 3 = 8
Reading file line by line:
Line: Hello, World!
Line: This is a text file.
Line: Line 3: 5 + 3 = 8
Data appended successfully.
Binary File Operations
Binary files store data in machine-readable format (raw bytes). They're more efficient for storing structured data like arrays and structures.
#include <stdio.h>
#include <stdlib.h>
// Structure definition
typedef struct {
int id;
char name[50];
float salary;
int age;
} Employee;
int main() {
FILE *file;
Employee emp, empRead;
// Create employee data
Employee employees[] = {
{101, "John Doe", 45000.50, 30},
{102, "Jane Smith", 52000.75, 28},
{103, "Bob Johnson", 38000.25, 35},
{104, "Alice Brown", 61000.00, 32},
{105, "Charlie Wilson", 48000.50, 29}
};
int numEmployees = 5;
// Write employees to binary file
file = fopen("employees.dat", "wb");
if(file == NULL) {
printf("Error opening file for writing!\n");
return 1;
}
fwrite(employees, sizeof(Employee), numEmployees, file);
fclose(file);
printf("%d employee records written to binary file.\n\n", numEmployees);
// Read employees from binary file
file = fopen("employees.dat", "rb");
if(file == NULL) {
printf("Error opening file for reading!\n");
return 1;
}
printf("Reading employee records:\n");
printf("ID\tName\t\tSalary\t\tAge\n");
printf("------------------------------------------------\n");
while(fread(&empRead, sizeof(Employee), 1, file) == 1) {
printf("%d\t%s\t%.2f\t%d\n",
empRead.id, empRead.name, empRead.salary, empRead.age);
}
fclose(file);
// Random access: Read specific record
printf("\nRandom access: Reading record #3 (index 2):\n");
file = fopen("employees.dat", "rb");
fseek(file, 2 * sizeof(Employee), SEEK_SET); // Move to 3rd record
fread(&empRead, sizeof(Employee), 1, file);
printf("Record #3: %d - %s - $%.2f - %d years old\n",
empRead.id, empRead.name, empRead.salary, empRead.age);
fclose(file);
// Get file size
file = fopen("employees.dat", "rb");
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
fclose(file);
printf("\nFile size: %ld bytes\n", fileSize);
printf("Size per employee: %lu bytes\n", sizeof(Employee));
printf("Total employees in file: %ld\n", fileSize / sizeof(Employee));
return 0;
}
File Error Handling
Proper error handling is crucial for robust file operations. Always check function return values and use error handling functions.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main() {
FILE *sourceFile, *destFile;
char sourceName[100], destName[100];
char ch;
printf("Enter source filename: ");
scanf("%s", sourceName);
printf("Enter destination filename: ");
scanf("%s", destName);
// Try to open source file
sourceFile = fopen(sourceName, "r");
if(sourceFile == NULL) {
printf("Error opening source file '%s': %s\n",
sourceName, strerror(errno));
return 1;
}
// Try to open destination file
destFile = fopen(destName, "w");
if(destFile == NULL) {
printf("Error opening destination file '%s': %s\n",
destName, strerror(errno));
fclose(sourceFile);
return 1;
}
// Clear error indicators
clearerr(sourceFile);
clearerr(destFile);
// Copy file content
printf("Copying file...\n");
while((ch = fgetc(sourceFile)) != EOF) {
if(fputc(ch, destFile) == EOF) {
if(ferror(destFile)) {
printf("Error writing to destination file!\n");
clearerr(destFile);
break;
}
}
}
// Check for read errors
if(ferror(sourceFile)) {
printf("Error reading from source file!\n");
}
// Check for EOF
if(feof(sourceFile)) {
printf("File copied successfully. End of file reached.\n");
}
// Close files
if(fclose(sourceFile) == EOF) {
printf("Error closing source file!\n");
}
if(fclose(destFile) == EOF) {
printf("Error closing destination file!\n");
}
// File operations
printf("\nFile operations:\n");
// Rename file
if(rename("oldname.txt", "newname.txt") == 0) {
printf("File renamed successfully.\n");
} else {
printf("Error renaming file: %s\n", strerror(errno));
}
// Delete file
printf("Enter filename to delete: ");
char deleteName[100];
scanf("%s", deleteName);
if(remove(deleteName) == 0) {
printf("File '%s' deleted successfully.\n", deleteName);
} else {
printf("Error deleting file '%s': %s\n", deleteName, strerror(errno));
}
return 0;
}
Best Practices for File Handling
File Handling Best Practices:
- Always check fopen() return value: Never assume file opened successfully
- Close files properly: Always call fclose() to free resources and flush buffers
- Use appropriate file modes: Choose the right mode for your operation
- Check for EOF properly: Use feof() after read operations fail
- Handle errors gracefully: Provide meaningful error messages
- Use binary mode for structured data: Use "b" mode for arrays and structures
- Check write operations: Verify that write operations succeeded
- Use buffered I/O wisely: Large buffers for large files, small for small files
- Validate file names: Check for invalid characters in file names
- Clean up temporary files: Remove temporary files when done
- Not checking if fopen() returned NULL
- Forgetting to close files (resource leak)
- Using text mode for binary data (corruption)
- Not handling partial read/write operations
- Assuming file position after operations
- Not clearing error indicators before reuse
- Using gets() instead of fgets() for safety
Key Takeaways
- File handling allows persistent data storage in C programs
- Always use FILE pointers to work with files
- Check fopen() return value for NULL before proceeding
- Text files store human-readable data, binary files store raw bytes
- Choose appropriate file modes (r, w, a, +, b combinations)
- Use fclose() to close files and free resources
- fread()/fwrite() for binary data, fprintf()/fscanf() for formatted text
- Use fseek(), ftell(), rewind() for random file access
- Implement error handling with feof(), ferror(), clearerr()
- Follow best practices for robust and secure file operations