File Operations
Master file operations in C: opening, closing, reading, writing files with fopen(), fclose(), fread(), fwrite(). Learn file modes, error handling, buffering, and best practices for reliable file I/O.
Understanding FILE Pointers
In C, files are accessed through FILE pointers. FILE is an opaque structure managed by the standard library. You open files to get a FILE pointer, perform operations through it, and close it when done. The FILE structure handles buffering, error tracking, and position tracking automatically.
#include <stdio.h>
/* FILE* is an opaque pointer */
/* You don't access FILE members directly */
int main(void) {
FILE *file;
/* Open file for reading */
file = fopen("example.txt", "r");
if (file == NULL) {
perror("Failed to open file");
return 1;
}
/* File operations... */
/* Always close when done */
fclose(file);
return 0;
}
/* Standard streams (always open) */
/* stdin - standard input (keyboard) */
/* stdout - standard output (screen) */
/* stderr - standard error (screen, unbuffered) */
void use_standard_streams(void) {
/* Read from stdin */
int value;
printf("Enter number: ");
scanf("%d", &value);
/* Write to stdout */
printf("You entered: %d\n", value);
/* Write to stderr */
fprintf(stderr, "Error message\n");
}Opening and Closing Files
fopen() opens files with specified modes: "r" (read), "w" (write), "a" (append), plus "b" for binary and "+" for read/write. Always check if fopen() returns NULL. Always close files with fclose() to flush buffers and release resources.
/* fopen() - open file */
FILE* fopen(const char *filename, const char *mode);
/* Returns:
- FILE* pointer on success
- NULL on failure (sets errno)
*/
/* File modes:
"r" - Read (file must exist)
"w" - Write (creates or truncates)
"a" - Append (creates if needed, writes at end)
"r+" - Read/write (file must exist)
"w+" - Read/write (creates or truncates)
"a+" - Read/append (creates if needed)
"rb" - Binary read
"wb" - Binary write
"ab" - Binary append
"r+b" - Binary read/write
etc...
*/
/* Opening for reading */
void open_read_example(void) {
FILE *file = fopen("input.txt", "r");
if (file == NULL) {
perror("fopen failed");
/* errno set automatically */
return;
}
printf("File opened successfully\n");
/* Use file... */
/* Close file */
if (fclose(file) != 0) {
perror("fclose failed");
}
}
/* Opening for writing */
void open_write_example(void) {
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
perror("Cannot create file");
return;
}
fprintf(file, "Hello, file!\n");
fclose(file);
}
/* Opening for appending */
void open_append_example(void) {
FILE *file = fopen("log.txt", "a");
if (file == NULL) {
perror("Cannot open log");
return;
}
fprintf(file, "Log entry\n");
fclose(file);
}
/* Binary mode */
void open_binary_example(void) {
FILE *file = fopen("data.bin", "rb");
if (file == NULL) {
perror("Cannot open binary file");
return;
}
/* Read binary data... */
fclose(file);
}
/* Read/write mode */
void open_read_write_example(void) {
FILE *file = fopen("data.txt", "r+");
if (file == NULL) {
/* File doesn't exist, create it */
file = fopen("data.txt", "w+");
if (file == NULL) {
perror("Cannot create file");
return;
}
}
/* Read and write... */
fclose(file);
}
/* Proper error handling */
int process_file(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
fprintf(stderr, "Error opening %s: %s\n",
filename, strerror(errno));
return -1;
}
/* Process file... */
int result = 0;
/* Close and check */
if (fclose(file) != 0) {
fprintf(stderr, "Error closing %s: %s\n",
filename, strerror(errno));
return -1;
}
return result;
}
/* Multiple files */
void multiple_files_example(void) {
FILE *input = fopen("input.txt", "r");
FILE *output = fopen("output.txt", "w");
if (input == NULL || output == NULL) {
/* Clean up */
if (input != NULL) fclose(input);
if (output != NULL) fclose(output);
return;
}
/* Copy input to output... */
fclose(input);
fclose(output);
}Reading from Files
C provides multiple functions for reading: fgetc() for characters, fgets() for lines, fscanf() for formatted input, fread() for binary blocks. Choose based on your data format. Always check return values for EOF and errors.
/* Reading characters */
int fgetc(FILE *stream); /* Returns character or EOF */
void read_chars_example(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
return;
}
int ch;
while ((ch = fgetc(file)) != EOF) {
putchar(ch); /* Echo to stdout */
}
if (ferror(file)) {
perror("Read error");
}
fclose(file);
}
/* Reading lines */
char* fgets(char *str, int n, FILE *stream);
void read_lines_example(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
return;
}
char line[256];
int line_num = 1;
while (fgets(line, sizeof(line), file) != NULL) {
printf("%d: %s", line_num++, line);
/* Remove newline if present */
size_t len = strlen(line);
if (len > 0 && line[len-1] == '\n') {
line[len-1] = '\0';
}
}
if (ferror(file)) {
perror("Read error");
}
fclose(file);
}
/* Formatted reading */
int fscanf(FILE *stream, const char *format, ...);
void read_formatted_example(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
return;
}
/* File contains: "Name: John Age: 30 Score: 95.5" */
char name[50];
int age;
float score;
if (fscanf(file, "Name: %s Age: %d Score: %f",
name, &age, &score) == 3) {
printf("Read: %s, %d, %.1f\n", name, age, score);
} else {
fprintf(stderr, "Format mismatch\n");
}
fclose(file);
}
/* Binary reading */
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
typedef struct {
int id;
char name[50];
float salary;
} Employee;
void read_binary_example(const char *filename) {
FILE *file = fopen(filename, "rb");
if (file == NULL) {
return;
}
Employee emp;
/* Read one Employee structure */
if (fread(&emp, sizeof(Employee), 1, file) == 1) {
printf("ID: %d, Name: %s, Salary: %.2f\n",
emp.id, emp.name, emp.salary);
} else {
if (feof(file)) {
printf("End of file\n");
} else {
perror("Read error");
}
}
fclose(file);
}
/* Reading entire file into memory */
char* read_entire_file(const char *filename, size_t *size) {
FILE *file = fopen(filename, "rb");
if (file == NULL) {
return NULL;
}
/* Get file size */
fseek(file, 0, SEEK_END);
long file_size = ftell(file);
fseek(file, 0, SEEK_SET);
if (file_size < 0) {
fclose(file);
return NULL;
}
/* Allocate buffer */
char *buffer = malloc(file_size + 1);
if (buffer == NULL) {
fclose(file);
return NULL;
}
/* Read entire file */
size_t bytes_read = fread(buffer, 1, file_size, file);
buffer[bytes_read] = '\0';
fclose(file);
if (size != NULL) {
*size = bytes_read;
}
return buffer;
}
/* Reading with error checking */
int safe_read_line(FILE *file, char *buffer, size_t size) {
if (fgets(buffer, size, file) == NULL) {
if (feof(file)) {
return 0; /* EOF */
} else {
return -1; /* Error */
}
}
return 1; /* Success */
}Writing to Files
Writing mirrors reading: fputc() for characters, fputs() for strings, fprintf() for formatted output, fwrite() for binary blocks. Always check return values. Call fflush() to force writing buffered data. Close files to ensure all data is written.
/* Writing characters */
int fputc(int c, FILE *stream);
void write_chars_example(const char *filename) {
FILE *file = fopen(filename, "w");
if (file == NULL) {
return;
}
for (char ch = 'A'; ch <= 'Z'; ch++) {
if (fputc(ch, file) == EOF) {
perror("Write error");
break;
}
}
fclose(file);
}
/* Writing strings */
int fputs(const char *str, FILE *stream);
void write_lines_example(const char *filename) {
FILE *file = fopen(filename, "w");
if (file == NULL) {
return;
}
const char *lines[] = {
"First line\n",
"Second line\n",
"Third line\n"
};
for (size_t i = 0; i < 3; i++) {
if (fputs(lines[i], file) == EOF) {
perror("Write error");
break;
}
}
fclose(file);
}
/* Formatted writing */
int fprintf(FILE *stream, const char *format, ...);
void write_formatted_example(const char *filename) {
FILE *file = fopen(filename, "w");
if (file == NULL) {
return;
}
fprintf(file, "Name: %s\n", "John Doe");
fprintf(file, "Age: %d\n", 30);
fprintf(file, "Score: %.2f\n", 95.5);
fclose(file);
}
/* Binary writing */
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
void write_binary_example(const char *filename) {
FILE *file = fopen(filename, "wb");
if (file == NULL) {
return;
}
Employee emp = {
.id = 1,
.name = "Alice",
.salary = 50000.0
};
if (fwrite(&emp, sizeof(Employee), 1, file) != 1) {
perror("Write error");
}
fclose(file);
}
/* Writing array of structures */
void write_array_binary(const char *filename, Employee *employees, size_t count) {
FILE *file = fopen(filename, "wb");
if (file == NULL) {
return;
}
size_t written = fwrite(employees, sizeof(Employee), count, file);
if (written != count) {
fprintf(stderr, "Only wrote %zu of %zu records\n", written, count);
}
fclose(file);
}
/* Appending to file */
void append_log(const char *filename, const char *message) {
FILE *file = fopen(filename, "a");
if (file == NULL) {
return;
}
time_t now = time(NULL);
fprintf(file, "[%s] %s\n", ctime(&now), message);
fclose(file);
}
/* Flushing buffers */
void flush_example(const char *filename) {
FILE *file = fopen(filename, "w");
if (file == NULL) {
return;
}
fprintf(file, "Important data\n");
fflush(file); /* Force write now */
/* Even if crash happens here, data is saved */
fprintf(file, "More data\n");
fclose(file); /* Flushes automatically */
}
/* Writing with error checking */
int safe_write(FILE *file, const void *data, size_t size) {
if (fwrite(data, 1, size, file) != size) {
if (ferror(file)) {
perror("Write error");
return -1;
}
}
return 0;
}
/* Copy file */
int copy_file(const char *src, const char *dest) {
FILE *in = fopen(src, "rb");
if (in == NULL) {
return -1;
}
FILE *out = fopen(dest, "wb");
if (out == NULL) {
fclose(in);
return -1;
}
char buffer[4096];
size_t bytes;
while ((bytes = fread(buffer, 1, sizeof(buffer), in)) > 0) {
if (fwrite(buffer, 1, bytes, out) != bytes) {
perror("Write error");
fclose(in);
fclose(out);
return -1;
}
}
if (ferror(in)) {
perror("Read error");
fclose(in);
fclose(out);
return -1;
}
fclose(in);
fclose(out);
return 0;
}File Position and Status
The FILE structure maintains a position indicator. fseek() moves it, ftell() reports it, rewind() resets it. feof() tests for end-of-file, ferror() tests for errors, clearerr() clears error flags. Understanding position management enables random access and status checking.
/* Get current position */
long ftell(FILE *stream); /* Returns position or -1L on error */
/* Set position */
int fseek(FILE *stream, long offset, int whence);
/* whence values:
SEEK_SET - From beginning
SEEK_CUR - From current position
SEEK_END - From end
*/
/* Position examples */
void position_example(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
return;
}
/* Get current position */
long pos = ftell(file);
printf("Current position: %ld\n", pos); /* 0 initially */
/* Read some data */
char buffer[10];
fread(buffer, 1, 10, file);
pos = ftell(file);
printf("After reading: %ld\n", pos); /* 10 */
/* Seek to beginning */
fseek(file, 0, SEEK_SET);
pos = ftell(file);
printf("After rewind: %ld\n", pos); /* 0 */
/* Seek to end */
fseek(file, 0, SEEK_END);
long size = ftell(file);
printf("File size: %ld bytes\n", size);
/* Seek to specific position */
fseek(file, 100, SEEK_SET); /* Byte 100 */
/* Relative seeking */
fseek(file, 10, SEEK_CUR); /* Forward 10 bytes */
fseek(file, -20, SEEK_CUR); /* Back 20 bytes */
fclose(file);
}
/* rewind() - reset to beginning */
void rewind(FILE *stream);
void rewind_example(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
return;
}
/* Read entire file */
char buffer[100];
while (fgets(buffer, sizeof(buffer), file) != NULL) {
/* Process */
}
/* Go back to start */
rewind(file);
/* Equivalent to: fseek(file, 0, SEEK_SET); */
/* Read again */
while (fgets(buffer, sizeof(buffer), file) != NULL) {
/* Process again */
}
fclose(file);
}
/* Check for EOF */
int feof(FILE *stream); /* Non-zero if EOF */
void eof_example(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
return;
}
char buffer[100];
while (fgets(buffer, sizeof(buffer), file) != NULL) {
printf("%s", buffer);
}
if (feof(file)) {
printf("Reached end of file\n");
} else if (ferror(file)) {
printf("Error occurred\n");
}
fclose(file);
}
/* Check for errors */
int ferror(FILE *stream); /* Non-zero if error */
void error_example(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
return;
}
int ch;
while ((ch = fgetc(file)) != EOF) {
putchar(ch);
if (ferror(file)) {
perror("Read error occurred");
break;
}
}
fclose(file);
}
/* Clear error indicators */
void clearerr(FILE *stream);
void clear_error_example(FILE *file) {
/* Error occurred */
if (ferror(file)) {
clearerr(file); /* Clear error flag */
/* Can try again */
}
}
/* Random access file */
void random_access_example(const char *filename) {
FILE *file = fopen(filename, "r+b"); /* Binary read/write */
if (file == NULL) {
return;
}
int value;
/* Read value at position 0 */
fseek(file, 0, SEEK_SET);
fread(&value, sizeof(int), 1, file);
printf("Value at 0: %d\n", value);
/* Read value at position 100 */
fseek(file, 100, SEEK_SET);
fread(&value, sizeof(int), 1, file);
printf("Value at 100: %d\n", value);
/* Write value at position 50 */
value = 42;
fseek(file, 50, SEEK_SET);
fwrite(&value, sizeof(int), 1, file);
fclose(file);
}
/* Get file size */
long get_file_size(const char *filename) {
FILE *file = fopen(filename, "rb");
if (file == NULL) {
return -1;
}
fseek(file, 0, SEEK_END);
long size = ftell(file);
fclose(file);
return size;
}Summary & What's Next
Key Takeaways:
- ✅ FILE* represents open files
- ✅ fopen() opens, fclose() closes files
- ✅ Always check if fopen() returns NULL
- ✅ Multiple functions for reading: fgetc, fgets, fscanf, fread
- ✅ Multiple functions for writing: fputc, fputs, fprintf, fwrite
- ✅ fseek() and ftell() enable random access
- ✅ Check feof() and ferror() after operations
- ✅ Always close files to flush buffers