File Handling
File Basics in C
Table of Contents
- β’Introduction
- β’What are Files?
- β’File Types in C
- β’File Pointer (FILE *)
- β’Opening Files - fopen()
- β’File Opening Modes
- β’Closing Files - fclose()
- β’Checking File Operations
- β’File Handle and Streams
- β’Standard Streams
- β’File Operations Workflow
- β’Common Pitfalls
- β’Best Practices
- β’Summary
Introduction
File handling is one of the most important aspects of programming. It allows programs to:
- β’Store data permanently - Data survives after program termination
- β’Process large datasets - Work with data too large for memory
- β’Share information - Exchange data between programs
- β’Maintain records - Create logs, configurations, databases
- β’Read external data - Process files created by other applications
C provides a comprehensive set of functions for file operations through the standard I/O library (<stdio.h>).
What are Files?
Definition
A file is a collection of related data stored on a secondary storage device (like a hard disk, SSD, or flash drive) with a unique name.
File Characteristics
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FILE β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Name : data.txt β
β Location : /home/user/documents/ β
β Size : 1024 bytes β
β Type : Text file β
β Created : 2024-01-15 10:30:00 β
β Modified : 2024-01-20 15:45:00 β
β Permissions: Read, Write β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Why Use Files?
| Aspect | Without Files | With Files |
|---|---|---|
| Data Persistence | Lost when program ends | Saved permanently |
| Data Size | Limited by RAM | Limited by disk space |
| Data Sharing | Difficult | Easy to share |
| Data Processing | All at once | Can process in chunks |
| Program State | Lost on restart | Can be restored |
File Types in C
C distinguishes between two main types of files:
1. Text Files
- β’Store data as human-readable characters
- β’Each line typically ends with a newline character (
\n) - β’Data is stored as ASCII/UTF-8 characters
- β’Can be viewed with any text editor
- β’Slightly larger size due to character encoding
Example content of a text file:
ββββββββββββββββββββββββββββββββββββββββββ
β Hello, World! β
β This is line 2. β
β Number: 42 β
ββββββββββββββββββββββββββββββββββββββββββ
Characteristics:
- β’Portable across different systems
- β’Easy to read and edit
- β’Slower to process
- β’Newline conversion (CR/LF) may occur
2. Binary Files
- β’Store data in raw binary format
- β’Exact memory representation of data
- β’Cannot be read with text editors (appears garbled)
- β’More compact and efficient
- β’Faster to read/write
Example binary representation:
ββββββββββββββββββββββββββββββββββββββββββ
β 48 65 6C 6C 6F 00 2A 00 00 00 β
β (Hello + null + 42 as integer) β
ββββββββββββββββββββββββββββββββββββββββββ
Characteristics:
- β’More efficient storage
- β’Faster read/write operations
- β’Not human-readable
- β’May not be portable between systems
Comparison
| Feature | Text File | Binary File |
|---|---|---|
| Readability | Human-readable | Machine-readable |
| Size | Larger | Smaller |
| Speed | Slower | Faster |
| Portability | Better | System-dependent |
| Data Types | Character only | Any data type |
| Editing | Easy | Requires special tools |
| Extension | .txt, .csv, .c | .bin, .dat, .exe |
File Pointer (FILE *)
The FILE Structure
In C, file operations use a file pointer of type FILE *. The FILE structure is defined in <stdio.h> and contains information about the file being accessed.
#include <stdio.h>
FILE *fp; // Declare a file pointer
What FILE Contains (Conceptual)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FILE Structure β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β βββββββββββββββββββ β
β β Buffer Pointer β β Points to I/O buffer β
β βββββββββββββββββββ€ β
β β Buffer Size β β Size of the buffer β
β βββββββββββββββββββ€ β
β β Buffer Count β β Bytes remaining in buffer β
β βββββββββββββββββββ€ β
β β File Descriptor β β OS-level file handle β
β βββββββββββββββββββ€ β
β β Flags β β Read/Write/EOF/Error status β
β βββββββββββββββββββ€ β
β β File Position β β Current position in file β
β βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Important Points
- β’Never access FILE members directly - Always use library functions
- β’FILE * is opaque - Implementation details are hidden
- β’Initialize to NULL - Good practice to initialize file pointers
- β’Check for NULL - Always verify fopen() succeeded
FILE *fp = NULL; // Good practice: initialize to NULL
fp = fopen("file.txt", "r");
if (fp == NULL) {
// Handle error
printf("Error: Cannot open file\n");
}
Opening Files - fopen()
Syntax
FILE *fopen(const char *filename, const char *mode);
Parameters
| Parameter | Description |
|---|---|
filename | Name of the file (with optional path) |
mode | String specifying how to open the file |
Return Value
- β’Success: Returns a pointer to FILE structure
- β’Failure: Returns NULL
Basic Usage
#include <stdio.h>
int main() {
FILE *fp;
// Open file for reading
fp = fopen("myfile.txt", "r");
if (fp == NULL) {
printf("Error: Could not open file\n");
return 1;
}
printf("File opened successfully!\n");
// ... perform file operations ...
fclose(fp); // Always close the file
return 0;
}
File Paths
// Current directory
fp = fopen("data.txt", "r");
// Relative path
fp = fopen("../data/input.txt", "r");
fp = fopen("subfolder/file.txt", "w");
// Absolute path (Linux/Mac)
fp = fopen("/home/user/documents/file.txt", "r");
// Absolute path (Windows)
fp = fopen("C:\\Users\\user\\Documents\\file.txt", "r");
// Or with forward slashes (works in Windows too)
fp = fopen("C:/Users/user/Documents/file.txt", "r");
File Opening Modes
Text Mode Options
| Mode | Description | File Exists | File Doesn't Exist |
|---|---|---|---|
"r" | Read only | Opens file | Returns NULL |
"w" | Write only | Truncates to 0 | Creates new file |
"a" | Append only | Opens, position at end | Creates new file |
"r+" | Read and write | Opens file | Returns NULL |
"w+" | Read and write | Truncates to 0 | Creates new file |
"a+" | Read and append | Opens file | Creates new file |
Binary Mode Options
Add b to the mode string for binary files:
| Mode | Description |
|---|---|
"rb" | Read binary |
"wb" | Write binary |
"ab" | Append binary |
"rb+" or "r+b" | Read and write binary |
"wb+" or "w+b" | Read and write binary (truncate) |
"ab+" or "a+b" | Read and append binary |
Mode Behavior Visual
Mode "r" (Read):
βββββββββββββββββββββββββββββββββββββββ
β existing content stays unchanged β
β ^ β
β Position starts at beginning β
βββββββββββββββββββββββββββββββββββββββ
Cannot write, only read.
Mode "w" (Write):
βββββββββββββββββββββββββββββββββββββββ
β (file is emptied/created) β
β ^ β
β Position starts at beginning β
βββββββββββββββββββββββββββββββββββββββ
Previous content is LOST!
Mode "a" (Append):
βββββββββββββββββββββββββββββββββββββββ
β existing content preserved β
β ^β
β Position always at end for writing β
βββββββββββββββββββββββββββββββββββββββ
New content added at end.
Mode "r+" (Read/Write):
βββββββββββββββββββββββββββββββββββββββ
β existing content stays β
β ^ β
β Can read and write anywhere β
βββββββββββββββββββββββββββββββββββββββ
Must call fseek() between read/write.
Mode "w+" (Read/Write, truncate):
βββββββββββββββββββββββββββββββββββββββ
β (file is emptied/created) β
β ^ β
β Can read and write β
βββββββββββββββββββββββββββββββββββββββ
Previous content is LOST!
Mode "a+" (Read/Append):
βββββββββββββββββββββββββββββββββββββββ
β existing content preserved β
β ^ β
β Can read from start, write at end β
βββββββββββββββββββββββββββββββββββββββ
Writes always go to end.
Choosing the Right Mode
Decision Tree:
βββββββββββββββββββββββ
β What operation? β
βββββββββββ¬ββββββββββββ
βββββββββββββββββΌββββββββββββββββ
βΌ βΌ βΌ
ββββββββββ ββββββββββ ββββββββββ
β Read β β Write β β Both β
βββββ¬βββββ βββββ¬βββββ βββββ¬βββββ
β β β
ββββββββββ΄βββββββββ β ββββββββββ΄βββββββββ
βΌ βΌ β βΌ βΌ
"r" or "rb" File must β Preserve Truncate
exist exist? β content? content?
β β β
ββββββββββββββ΄β βΌ βΌ
βΌ βΌ "r+"/rb+" "w+"/wb+"
Create new Truncate
or append? existing?
β β
βββββββ΄ββββββ βΌ
βΌ βΌ "w"/"wb"
"a"/"ab" "w"/"wb"
Closing Files - fclose()
Syntax
int fclose(FILE *stream);
Return Value
- β’Success: Returns 0
- β’Failure: Returns EOF (typically -1)
Why Close Files?
- β’Flush Buffers: Ensures all data is written to disk
- β’Release Resources: Frees file handle and memory
- β’Prevent Data Loss: Unsaved buffer data may be lost
- β’Avoid File Limits: Systems limit open files
- β’Allow Other Access: Release locks on the file
Basic Usage
#include <stdio.h>
int main() {
FILE *fp = fopen("data.txt", "w");
if (fp == NULL) {
return 1;
}
fprintf(fp, "Hello, World!\n");
// Close the file
if (fclose(fp) != 0) {
printf("Error closing file\n");
return 1;
}
// Set pointer to NULL after closing (good practice)
fp = NULL;
return 0;
}
Common Mistakes with fclose()
// WRONG: Using file after closing
FILE *fp = fopen("test.txt", "r");
fclose(fp);
fgetc(fp); // UNDEFINED BEHAVIOR!
// WRONG: Closing NULL pointer
FILE *fp = fopen("nonexistent.txt", "r"); // Returns NULL
fclose(fp); // UNDEFINED BEHAVIOR!
// WRONG: Closing same file twice
FILE *fp = fopen("test.txt", "r");
fclose(fp);
fclose(fp); // UNDEFINED BEHAVIOR!
// CORRECT approach:
FILE *fp = fopen("test.txt", "r");
if (fp != NULL) {
// ... use file ...
fclose(fp);
fp = NULL; // Prevent accidental reuse
}
Checking File Operations
Error Checking Pattern
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main() {
FILE *fp;
const char *filename = "data.txt";
// Open file
fp = fopen(filename, "r");
if (fp == NULL) {
// Method 1: Simple message
printf("Error: Cannot open '%s'\n", filename);
// Method 2: Using perror (prints system error message)
perror("fopen failed");
// Method 3: Using strerror
printf("Error: %s\n", strerror(errno));
return 1;
}
printf("File opened successfully\n");
// Close file
if (fclose(fp) == EOF) {
perror("fclose failed");
return 1;
}
return 0;
}
Common errno Values for File Operations
| Error | Value | Description |
|---|---|---|
| ENOENT | 2 | No such file or directory |
| EACCES | 13 | Permission denied |
| EEXIST | 17 | File exists (when creating) |
| EISDIR | 21 | Is a directory |
| EMFILE | 24 | Too many open files |
| ENOSPC | 28 | No space left on device |
| EROFS | 30 | Read-only file system |
Using ferror() and feof()
#include <stdio.h>
void check_file_status(FILE *fp) {
if (feof(fp)) {
printf("End of file reached\n");
}
if (ferror(fp)) {
printf("An error occurred\n");
clearerr(fp); // Clear error indicator
}
}
File Handle and Streams
Buffering Concept
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Buffered I/O β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Program Buffer Disk β
β βββββββ βββββββββ βββββββ β
β β β write() β β flush β β β
β β β βββββββββββΊ β ##### β βββββββββββΊ β β β
β β β β ##### β β β β
β β β read() β β fill β β β
β β β βββββββββββ β ##### β βββββββββββ β β β
β βββββββ βββββββββ βββββββ β
β β
β Data is collected in buffer before being written to disk. β
β This reduces the number of slow disk operations. β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Buffer Types
- β’
Fully Buffered: Data written when buffer is full
- β’Default for files
- β’Most efficient for files
- β’
Line Buffered: Data written when newline is encountered
- β’Default for terminals (stdout when connected to terminal)
- β’Good for interactive output
- β’
Unbuffered: Data written immediately
- β’Used for stderr
- β’Slower but immediate
Controlling Buffering
#include <stdio.h>
int main() {
FILE *fp = fopen("output.txt", "w");
// Set buffer size (must be called before any I/O)
char buffer[1024];
setvbuf(fp, buffer, _IOFBF, sizeof(buffer)); // Fully buffered
// Or use setbuf
// setbuf(fp, buffer); // Enable buffering
// setbuf(fp, NULL); // Disable buffering
// Alternative: use buffer modes
// setvbuf(fp, NULL, _IONBF, 0); // Unbuffered
// setvbuf(fp, NULL, _IOLBF, 0); // Line buffered
// setvbuf(fp, NULL, _IOFBF, 0); // Fully buffered (system size)
fprintf(fp, "Data\n");
fclose(fp);
return 0;
}
Flushing the Buffer
#include <stdio.h>
int main() {
FILE *fp = fopen("log.txt", "w");
fprintf(fp, "Important message");
// Force write to disk immediately
fflush(fp);
// ... program continues, data is safe on disk ...
fclose(fp);
return 0;
}
Standard Streams
C automatically opens three standard streams when a program starts:
The Three Standard Streams
| Stream | Pointer | Default | Purpose |
|---|---|---|---|
| Standard Input | stdin | Keyboard | Read user input |
| Standard Output | stdout | Screen | Normal program output |
| Standard Error | stderr | Screen | Error messages |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Standard Streams β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββ stdin βββββββββββ β
β β Keyboard β βββββββββββββββββΊ β β β
β ββββββββββββ β β β
β β Program β β
β ββββββββββββ stdout β β β
β β Screen β βββββββββββββββββ β β β
β β β stderr β β β
β β β βββββββββββββββββ β β β
β ββββββββββββ βββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Using Standard Streams
#include <stdio.h>
int main() {
char name[50];
// stdin - reading input
fprintf(stdout, "Enter your name: "); // Same as printf
fscanf(stdin, "%49s", name); // Same as scanf
// stdout - normal output
fprintf(stdout, "Hello, %s!\n", name); // Same as printf
// stderr - error output
fprintf(stderr, "Warning: This is an error message\n");
return 0;
}
Redirecting Streams
# In shell/command line:
# Redirect stdout to file
./program > output.txt
# Redirect stderr to file
./program 2> errors.txt
# Redirect both
./program > output.txt 2> errors.txt
# Redirect stdin from file
./program < input.txt
# Combine all
./program < input.txt > output.txt 2> errors.txt
Reopening Standard Streams
#include <stdio.h>
int main() {
// Redirect stdout to a file
FILE *fp = freopen("output.txt", "w", stdout);
if (fp == NULL) {
fprintf(stderr, "Failed to redirect stdout\n");
return 1;
}
// Now printf writes to file
printf("This goes to output.txt\n");
// Restore stdout (platform-specific)
// On Linux: freopen("/dev/tty", "w", stdout);
// On Windows: freopen("CON", "w", stdout);
fclose(fp);
return 0;
}
File Operations Workflow
Complete File Handling Pattern
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp = NULL;
int result = 0;
// Step 1: Open the file
fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return EXIT_FAILURE;
}
// Step 2: Perform operations
// ... read, write, or other operations ...
// Step 3: Check for errors during operations
if (ferror(fp)) {
fprintf(stderr, "Error during file operations\n");
result = EXIT_FAILURE;
}
// Step 4: Close the file
if (fclose(fp) != 0) {
perror("Error closing file");
result = EXIT_FAILURE;
}
// Step 5: Nullify pointer
fp = NULL;
return result;
}
Workflow Diagram
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β File Operations Workflow β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββ β
β β START β β
β ββββββββ¬βββββββ β
β β β
β βΌ β
β βββββββββββββββββββ βββββββββββββββββ β
β β fopen(file) ββNOβββΊβ Handle Error β β
β β fp != NULL? β β (print, exit) β β
β βββββββββ¬ββββββββββ βββββββββββββββββ β
β βYES β
β βΌ β
β βββββββββββββββββββ β
β β Perform File βββββββββββββββββββββ β
β β Operations β β β
β β (read/write) βββββββββββββββββββββ€ β
β βββββββββ¬ββββββββββ β β
β β β β
β βΌ β β
β βββββββββββββββββββ YES β β
β β More ops? βββββββββββββββββββββ β
β βββββββββ¬ββββββββββ β
β βNO β
β βΌ β
β βββββββββββββββββββ βββββββββββββββββ β
β β fclose(fp) ββERRββΊβ Handle Error β β
β β == 0? β β (log warning) β β
β βββββββββ¬ββββββββββ βββββββββ¬ββββββββ β
β βOK β β
β βΌ β β
β βββββββββββββββββββ β β
β β fp = NULL ββββββββββββββββ β
β βββββββββ¬ββββββββββ β
β β β
β βΌ β
β βββββββββββββββ β
β β END β β
β βββββββββββββββ β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Common Pitfalls
1. Not Checking fopen() Return Value
// WRONG - dangerous!
FILE *fp = fopen("file.txt", "r");
fprintf(fp, "data"); // CRASH if file doesn't exist!
// CORRECT
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
perror("Cannot open file");
return 1;
}
fprintf(fp, "data");
2. Forgetting to Close Files
// WRONG - resource leak
void process_files() {
for (int i = 0; i < 1000; i++) {
char filename[50];
sprintf(filename, "file%d.txt", i);
FILE *fp = fopen(filename, "r");
// ... process ...
// Forgot fclose(fp)! // Eventually runs out of file handles
}
}
// CORRECT
void process_files() {
for (int i = 0; i < 1000; i++) {
char filename[50];
sprintf(filename, "file%d.txt", i);
FILE *fp = fopen(filename, "r");
if (fp != NULL) {
// ... process ...
fclose(fp);
}
}
}
3. Using Wrong Mode
// WRONG - "w" destroys existing content!
FILE *fp = fopen("important_data.txt", "w"); // File is now EMPTY!
// CORRECT - use "a" to append or "r+" to modify
FILE *fp = fopen("important_data.txt", "a"); // Adds to existing
// OR
FILE *fp = fopen("important_data.txt", "r+"); // Read/write without truncating
4. Mixing Text and Binary Modes
// WRONG - writing binary data in text mode
struct Data { int x; float y; } data = {42, 3.14};
FILE *fp = fopen("data.dat", "w"); // Text mode!
fwrite(&data, sizeof(data), 1, fp); // May corrupt on Windows
// CORRECT - use binary mode for binary data
FILE *fp = fopen("data.dat", "wb"); // Binary mode
fwrite(&data, sizeof(data), 1, fp);
5. Not Handling Paths Correctly
// Platform-specific issues
// Windows uses backslashes, Linux uses forward slashes
// PORTABLE approach - use forward slashes (works on both)
FILE *fp = fopen("data/input/file.txt", "r");
// Or use path construction
char path[256];
snprintf(path, sizeof(path), "data%cinput%cfile.txt",
'/', '/'); // Use '/' for portability
Best Practices
1. Always Check Return Values
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
// Handle error appropriately
perror("Failed to open file");
return -1;
}
2. Use Meaningful Variable Names
// POOR
FILE *f;
// BETTER
FILE *inputFile;
FILE *outputFile;
FILE *configFile;
3. Close Files as Soon as Possible
// Read all data, then close
FILE *fp = fopen("data.txt", "r");
if (fp != NULL) {
// Read all needed data
char buffer[1000];
fgets(buffer, sizeof(buffer), fp);
// Close immediately after reading
fclose(fp);
fp = NULL;
// Process data (file is already closed)
process(buffer);
}
4. Use Helper Functions
// Create reusable file opening function
FILE *open_file_safe(const char *filename, const char *mode) {
FILE *fp = fopen(filename, mode);
if (fp == NULL) {
fprintf(stderr, "Error: Cannot open '%s' with mode '%s': %s\n",
filename, mode, strerror(errno));
}
return fp;
}
// Usage
FILE *fp = open_file_safe("data.txt", "r");
if (fp != NULL) {
// ... use file ...
fclose(fp);
}
5. Consider Using goto for Cleanup
int process_file(const char *filename) {
FILE *fp = NULL;
char *buffer = NULL;
int result = -1;
fp = fopen(filename, "r");
if (fp == NULL) goto cleanup;
buffer = malloc(1000);
if (buffer == NULL) goto cleanup;
// ... process ...
result = 0; // Success
cleanup:
if (buffer != NULL) free(buffer);
if (fp != NULL) fclose(fp);
return result;
}
Summary
Key Concepts
| Concept | Description |
|---|---|
| FILE * | Pointer to file structure, used for all operations |
| fopen() | Opens a file, returns FILE * or NULL |
| fclose() | Closes a file, flushes buffers, returns 0 or EOF |
| Modes | "r", "w", "a", "r+", "w+", "a+" (add "b" for binary) |
| Streams | stdin, stdout, stderr are predefined FILE pointers |
| Buffering | Data collected in memory before disk operations |
Mode Quick Reference
| Mode | Exists | Not Exists | Position | Truncate |
|---|---|---|---|---|
| r | Read | Error | Start | No |
| w | Write | Create | Start | Yes |
| a | Write | Create | End | No |
| r+ | R/W | Error | Start | No |
| w+ | R/W | Create | Start | Yes |
| a+ | R/W | Create | End(w) | No |
Essential Error Checking
// Minimal safe pattern
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
perror("fopen");
return 1;
}
// ... operations ...
if (fclose(fp) != 0) {
perror("fclose");
return 1;
}
What's Next?
In the following sections, we'll explore:
- β’Reading from files (fgetc, fgets, fscanf, fread)
- β’Writing to files (fputc, fputs, fprintf, fwrite)
- β’File positioning (fseek, ftell, rewind)
- β’Binary file operations
- β’Error handling and file status
Practice Exercises Preview
- β’Open a file and check if it exists
- β’Create a new file with sample content
- β’Handle file opening errors gracefully
- β’Work with different file modes
- β’Redirect stdout to a file