Chapter 13: File Management in C

📌 Learning Objectives
  • LO 13.1: Define and open files using fopen()
  • LO 13.2: Perform input/output operations on files
  • LO 13.3: Handle errors during file operations
  • LO 13.4: Implement random access to files
  • LO 13.5: Use command line arguments in programs

13.1 Introduction to File Management

Until now, we have been using console-oriented I/O functions (scanf, printf) which use the terminal as the target. However, real-life problems involve large volumes of data that need to be stored permanently on disks. File operations in C allow us to:

Basic file operations include:

13.2 File Pointers and the FILE Structure

All file operations in C use a special data type called FILE, defined in stdio.h. A file pointer is used to refer to an opened file.

FILE *fp;  // Declare a file pointer

13.3 Opening a File: fopen()

The fopen() function is used to open a file. It returns a file pointer that is used for subsequent file operations.

FILE *fopen(const char *filename, const char *mode);

File opening modes:

Mode Description
"r" Open for reading. File must exist.
"w" Open for writing. Creates new file or truncates existing file.
"a" Open for appending. Creates new file if it doesn't exist.
"r+" Open for reading and writing. File must exist.
"w+" Open for reading and writing. Creates new file or truncates existing.
"a+" Open for reading and appending. Creates new file if it doesn't exist.
"rb", "wb", "ab" Binary modes (similar to text modes but for binary files)

📝 Worked-Out Problem 13.1

Opening a file for writing:

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *fp;
    
    fp = fopen("test.txt", "w");
    
    if (fp == NULL) {
        printf("Error opening file!\n");
        exit(1);
    }
    
    printf("File opened successfully!\n");
    fprintf(fp, "Hello, World!\n");
    
    fclose(fp);
    return 0;
}
Output:
File opened successfully!
(Creates test.txt with content "Hello, World!")

13.4 Closing a File: fclose()

Always close a file after completing operations. This flushes buffers and breaks the link to the file.

int fclose(FILE *stream);  // Returns 0 on success, EOF on error

13.5 Character I/O: getc() and putc()

These functions read and write a single character at a time.

int getc(FILE *stream);      // Returns character or EOF
int putc(int ch, FILE *stream);

📝 Worked-Out Problem 13.2

Character-oriented file operations:

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *f1, *f2;
    char ch;
    
    f1 = fopen("input.txt", "w");
    if (f1 == NULL) {
        printf("Cannot create file\n");
        exit(1);
    }
    
    printf("Enter text (Ctrl+Z to end):\n");
    while ((ch = getchar()) != EOF)
        putc(ch, f1);
    
    fclose(f1);
    
    f1 = fopen("input.txt", "r");
    f2 = fopen("output.txt", "w");
    
    if (f1 == NULL || f2 == NULL) {
        printf("File error\n");
        exit(1);
    }
    
    while ((ch = getc(f1)) != EOF)
        putc(ch, f2);
    
    printf("File copied successfully!\n");
    
    fclose(f1);
    fclose(f2);
    return 0;
}

13.6 String I/O: fgets() and fputs()

These functions read and write strings line by line.

char *fgets(char *str, int n, FILE *stream);  // Reads at most n-1 characters
int fputs(const char *str, FILE *stream);

📝 Worked-Out Problem 13.3

Reading and writing strings to files:

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *fp;
    char str[100];
    
    fp = fopen("data.txt", "w");
    if (fp == NULL) {
        printf("Cannot open file\n");
        exit(1);
    }
    
    printf("Enter lines of text (empty line to stop):\n");
    while (1) {
        fgets(str, 100, stdin);
        if (str[0] == '\n')
            break;
        fputs(str, fp);
    }
    
    fclose(fp);
    
    fp = fopen("data.txt", "r");
    printf("\nFile contents:\n");
    while (fgets(str, 100, fp) != NULL)
        printf("%s", str);
    
    fclose(fp);
    return 0;
}

13.7 Formatted I/O: fprintf() and fscanf()

These functions work like printf() and scanf() but operate on files.

int fprintf(FILE *stream, const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);

📝 Worked-Out Problem 13.4

Writing and reading mixed data types:

#include <stdio.h>
#include <stdlib.h>

struct student {
    int roll;
    char name[30];
    float marks;
};

int main() {
    FILE *fp;
    struct student s1 = {101, "John", 85.5};
    struct student s2;
    
    fp = fopen("student.dat", "w");
    if (fp == NULL) {
        printf("Cannot open file\n");
        exit(1);
    }
    
    fprintf(fp, "%d %s %.2f\n", s1.roll, s1.name, s1.marks);
    fclose(fp);
    
    fp = fopen("student.dat", "r");
    fscanf(fp, "%d %s %f", &s2.roll, s2.name, &s2.marks);
    
    printf("Read from file:\n");
    printf("Roll: %d\n", s2.roll);
    printf("Name: %s\n", s2.name);
    printf("Marks: %.2f\n", s2.marks);
    
    fclose(fp);
    return 0;
}
Output:
Read from file:
Roll: 101
Name: John
Marks: 85.50

13.8 Integer I/O: getw() and putw()

These functions read and write integers to files. Note: These are not standard ANSI functions but are available in many compilers.

📝 Worked-Out Problem 13.5

Reading and writing integers using getw/putw:

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *f1, *f2, *f3;
    int number, i;
    
    f1 = fopen("DATA", "w");
    printf("Enter numbers (-1 to stop):\n");
    scanf("%d", &number);
    
    while (number != -1) {
        putw(number, f1);
        scanf("%d", &number);
    }
    fclose(f1);
    
    f1 = fopen("DATA", "r");
    f2 = fopen("ODD", "w");
    f3 = fopen("EVEN", "w");
    
    while ((number = getw(f1)) != EOF) {
        if (number % 2 == 0)
            putw(number, f3);
        else
            putw(number, f2);
    }
    
    fclose(f1);
    fclose(f2);
    fclose(f3);
    
    printf("\nODD numbers:\n");
    f2 = fopen("ODD", "r");
    while ((number = getw(f2)) != EOF)
        printf("%d ", number);
    
    printf("\nEVEN numbers:\n");
    f3 = fopen("EVEN", "r");
    while ((number = getw(f3)) != EOF)
        printf("%d ", number);
    
    printf("\n");
    fclose(f2);
    fclose(f3);
    return 0;
}
Output:
Enter numbers (-1 to stop):
10 15 22 37 44 51 -1

ODD numbers:
15 37 51
EVEN numbers:
10 22 44

13.9 Error Handling: feof() and ferror()

These functions help detect errors during file operations and end-of-file conditions.

int feof(FILE *stream);    // Returns non-zero if end of file reached
int ferror(FILE *stream);  // Returns non-zero if error occurred

📝 Worked-Out Problem 13.6

Error handling in file operations:

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *fp;
    char filename[50];
    char ch;
    
    printf("Enter filename: ");
    scanf("%s", filename);
    
    fp = fopen(filename, "r");
    if (fp == NULL) {
        printf("Cannot open file %s\n", filename);
        exit(1);
    }
    
    while (!feof(fp)) {
        ch = fgetc(fp);
        if (ferror(fp)) {
            printf("Error reading from file\n");
            break;
        }
        if (!feof(fp))
            putchar(ch);
    }
    
    fclose(fp);
    return 0;
}

13.10 Random Access to Files

Random access allows moving to any position in a file, not just sequential reading.

ftell() - Get current file position

long ftell(FILE *stream);  // Returns current file position

rewind() - Reset to beginning

void rewind(FILE *stream);  // Sets file position to beginning

fseek() - Move to specific position

int fseek(FILE *stream, long offset, int whence);

Whence values:

OperationCode
Skip n bytes forwardfseek(fp, n, SEEK_CUR);
Go to beginningfseek(fp, 0, SEEK_SET);
Go to endfseek(fp, 0, SEEK_END);
Go to nth byte from startfseek(fp, n, SEEK_SET);
Go back n bytesfseek(fp, -n, SEEK_CUR);
Go to nth byte from endfseek(fp, -n, SEEK_END);

📝 Worked-Out Problem 13.7

Random access using fseek and ftell:

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *fp;
    char ch;
    long position;
    
    fp = fopen("sample.txt", "w+");
    fprintf(fp, "This is a sample file for random access demonstration.");
    rewind(fp);  // Go to beginning
    
    printf("File created. Size: %ld bytes\n\n", ftell(fp));
    
    // Read and display every 5th character
    printf("Every 5th character:\n");
    for (position = 4; ; position += 5) {
        fseek(fp, position, SEEK_SET);
        if (feof(fp)) break;
        ch = fgetc(fp);
        if (!feof(fp))
            printf("Position %ld: %c\n", position, ch);
    }
    
    // Read from the end
    printf("\nReading from the end:\n");
    fseek(fp, -2, SEEK_END);
    position = ftell(fp);
    printf("Second last character at position %ld: %c\n", 
           position, fgetc(fp));
    
    fclose(fp);
    return 0;
}

📝 Worked-Out Problem 13.8

Appending items to an existing file:

#include <stdio.h>
#include <stdlib.h>

struct item {
    char name[20];
    int quantity;
    float price;
};

int main() {
    FILE *fp;
    struct item it;
    char filename[50];
    long n;
    char ch;
    
    printf("Enter filename: ");
    scanf("%s", filename);
    
    fp = fopen(filename, "a+");
    if (fp == NULL) {
        printf("Cannot open file\n");
        exit(1);
    }
    
    do {
        printf("\nEnter item name: ");
        scanf("%s", it.name);
        printf("Enter quantity: ");
        scanf("%d", &it.quantity);
        printf("Enter price: ");
        scanf("%f", &it.price);
        
        fprintf(fp, "%s %d %.2f\n", it.name, it.quantity, it.price);
        
        printf("Add another item? (y/n): ");
        scanf(" %c", &ch);
    } while (ch == 'y' || ch == 'Y');
    
    n = ftell(fp);  // Save current position
    fclose(fp);
    
    // Display file contents
    fp = fopen(filename, "r");
    printf("\nFile contents:\n");
    while (fscanf(fp, "%s %d %f", it.name, &it.quantity, &it.price) == 3) {
        printf("%s\t%d\t%.2f\n", it.name, it.quantity, it.price);
    }
    fclose(fp);
    
    return 0;
}

13.11 Command Line Arguments

Command line arguments allow passing parameters to a program when it is executed. The main() function can take two arguments:

int main(int argc, char *argv[])

📝 Worked-Out Problem 13.9

Displaying command line arguments:

#include <stdio.h>

int main(int argc, char *argv[]) {
    int i;
    
    printf("Program name: %s\n", argv[0]);
    printf("Number of arguments: %d\n", argc);
    
    if (argc > 1) {
        printf("Arguments:\n");
        for (i = 1; i < argc; i++)
            printf("argv[%d] = %s\n", i, argv[i]);
    } else {
        printf("No arguments provided.\n");
    }
    
    return 0;
}
Compile and run:
gcc program.c -o program
./program hello world 123

Output:
Program name: ./program
Number of arguments: 4
Arguments:
argv[1] = hello
argv[2] = world
argv[3] = 123

📝 Worked-Out Problem 13.10

File copy program using command line arguments:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    FILE *source, *target;
    char ch;
    
    if (argc != 3) {
        printf("Usage: %s source_file target_file\n", argv[0]);
        exit(1);
    }
    
    source = fopen(argv[1], "r");
    if (source == NULL) {
        printf("Cannot open source file %s\n", argv[1]);
        exit(1);
    }
    
    target = fopen(argv[2], "w");
    if (target == NULL) {
        printf("Cannot create target file %s\n", argv[2]);
        fclose(source);
        exit(1);
    }
    
    while ((ch = fgetc(source)) != EOF)
        fputc(ch, target);
    
    printf("File copied successfully!\n");
    
    fclose(source);
    fclose(target);
    return 0;
}

📝 Worked-Out Problem 13.11

Program to reverse characters in a file:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    FILE *fp;
    long i;
    char ch;
    
    if (argc != 2) {
        printf("Usage: %s filename\n", argv[0]);
        exit(1);
    }
    
    fp = fopen(argv[1], "r+");
    if (fp == NULL) {
        printf("Cannot open file %s\n", argv[1]);
        exit(1);
    }
    
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    
    printf("Reversing %ld characters in %s...\n", size, argv[1]);
    
    for (i = 0; i < size/2; i++) {
        char ch1, ch2;
        
        // Read character from beginning
        fseek(fp, i, SEEK_SET);
        ch1 = fgetc(fp);
        
        // Read character from end
        fseek(fp, size - 1 - i, SEEK_SET);
        ch2 = fgetc(fp);
        
        // Swap them
        fseek(fp, i, SEEK_SET);
        fputc(ch2, fp);
        fseek(fp, size - 1 - i, SEEK_SET);
        fputc(ch1, fp);
    }
    
    printf("File reversed successfully.\n");
    fclose(fp);
    return 0;
}

13.12 Binary File Operations

For binary files, we use fread() and fwrite() functions.

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

📝 Worked-Out Problem 13.12

Writing and reading structures to binary file:

#include <stdio.h>
#include <stdlib.h>

struct record {
    int id;
    char name[30];
    float salary;
};

int main() {
    FILE *fp;
    struct record emp1 = {101, "John Doe", 45000.50};
    struct record emp2;
    
    fp = fopen("employee.dat", "wb");
    if (fp == NULL) {
        printf("Cannot create file\n");
        exit(1);
    }
    
    fwrite(&emp1, sizeof(struct record), 1, fp);
    fclose(fp);
    
    fp = fopen("employee.dat", "rb");
    fread(&emp2, sizeof(struct record), 1, fp);
    
    printf("Read from binary file:\n");
    printf("ID: %d\n", emp2.id);
    printf("Name: %s\n", emp2.name);
    printf("Salary: %.2f\n", emp2.salary);
    
    fclose(fp);
    return 0;
}

Important Points to Remember

  • Always check if file opened successfully (fp != NULL).
  • Close files after use to prevent data loss and free resources.
  • When opening a file in "w" mode, existing contents are deleted.
  • Use "a" mode to append to existing files without losing data.
  • Check for EOF when reading files to avoid infinite loops.
  • Use feof() to test for end-of-file condition.
  • Random access functions return -1 on error; always check return values.
  • Binary files are more efficient for storing structured data.
  • Command line arguments are space-separated; use quotes for arguments with spaces.

Chapter Exercises

Review Questions

  1. State true or false:
    a) A file must be opened before it can be used.
    b) All files must be explicitly closed.
    c) Files are always referred to by name in C programs.
    d) Using fseek to position a file beyond the end is an error.
    e) If an existing file is opened in write mode, its contents are deleted.
    f) If a non-existent file is opened in read mode, it results in an error.
    g) Both scanf() and fscanf() return the number of items successfully read.
  2. What is the difference between getc() and getchar()?
  3. How does append mode differ from write mode?
  4. What are the common uses of rewind() and ftell()?
  5. Explain the general format of fseek() function.
  6. What does while ((c = getchar()) != EOF) do?

Multiple Choice Questions

  1. Which function is used to write an integer to a file?
    a) fprintf()
    b) putw()
    c) putc()
    d) puts()
  2. Which symbol indicates EOF?
    a) ^Z
    b) ~Z
    c) EOF
    d) All of these
  3. While reading integer data from a file, EOF is indicated by which value?
    a) -1
    b) 0
    c) -9999
    d) None
  4. Which status-enquiry function detects I/O errors?
    a) feof()
    b) error()
    c) ferror()
    d) None
  5. The mode _______ is used for opening a file for updating.
    a) "r+"
    b) "w"
    c) "a"
    d) "rb"

Debugging Exercises

Find errors in the following code segments:

FILE fptr;
fptr = fopen("data.txt", "r");  // Error?

FILE *fp = fopen("test.txt", "r");
fprintf(fp, "Hello");  // Error?

fclose("data.txt");  // Error?

FILE *fp = fopen("nonexistent.txt", "r");
while (!feof(fp)) {
    fgetc(fp);  // Error?
}

Programming Exercises

  1. Write a program to copy the contents of one file to another.
  2. Two files DATA1 and DATA2 contain sorted lists of integers. Write a program to merge them into a third sorted file. Use command line arguments for file names.
  3. Write a program that compares two files and returns 0 if they are equal, 1 if not.
  4. Write a program that appends one file at the end of another.
  5. Write a program that reads a file containing integers and appends the sum of all integers at the end.
  6. Write a program that prompts for two files - source and target - and copies source to target, deleting a specified character during copying.
  7. Write a program that requests a filename and an offset value, then reads and prints the file starting from that offset.
  8. Write a program to create a sequential file storing product details (code, cost, quantity).
  9. Write a program to read the file created in Exercise 8 and compute the total value of all products.
  10. Write a program to store records in a random access file and print alternate records.
  11. Write a program that uses getw() to read integers from one file and writes them in reverse order to another.
  12. Write a program that reads characters from a file and prints their ASCII codes.

Interview Questions

  1. What is the difference between printf() and fprintf()?
  2. What is the difference between getc(), getw(), and fscanf()?
  3. What is the purpose of ftell() function?
  4. What is the purpose of fseek() function?
  5. What is the difference between printf() and sprintf()?
  6. What will happen if you try to open a file for reading that doesn't exist?
  7. How do you test for end-of-file while reading?
  8. What is the difference between text mode and binary mode?

Case Study: Inventory Management System

📝 Case Study: Complete inventory management with file operations

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct item {
    int code;
    char name[30];
    int quantity;
    float price;
};

void add_item() {
    FILE *fp;
    struct item it;
    char ch;
    
    fp = fopen("inventory.dat", "ab");
    if (fp == NULL) {
        printf("Cannot open file\n");
        return;
    }
    
    do {
        printf("\nEnter item code: ");
        scanf("%d", &it.code);
        printf("Enter item name: ");
        scanf("%s", it.name);
        printf("Enter quantity: ");
        scanf("%d", &it.quantity);
        printf("Enter price: ");
        scanf("%f", &it.price);
        
        fwrite(&it, sizeof(struct item), 1, fp);
        
        printf("Add another? (y/n): ");
        scanf(" %c", &ch);
    } while (ch == 'y' || ch == 'Y');
    
    fclose(fp);
}

void display_all() {
    FILE *fp;
    struct item it;
    int count = 0;
    
    fp = fopen("inventory.dat", "rb");
    if (fp == NULL) {
        printf("No inventory file found\n");
        return;
    }
    
    printf("\n%5s %-20s %10s %10s %10s\n", 
           "Code", "Name", "Quantity", "Price", "Value");
    printf("------------------------------------------------\n");
    
    while (fread(&it, sizeof(struct item), 1, fp) == 1) {
        printf("%5d %-20s %10d %10.2f %10.2f\n", 
               it.code, it.name, it.quantity, it.price, 
               it.quantity * it.price);
        count++;
    }
    
    printf("------------------------------------------------\n");
    printf("Total items: %d\n", count);
    
    fclose(fp);
}

void search_item() {
    FILE *fp;
    struct item it;
    int code, found = 0;
    
    printf("Enter item code to search: ");
    scanf("%d", &code);
    
    fp = fopen("inventory.dat", "rb");
    if (fp == NULL) {
        printf("No inventory file found\n");
        return;
    }
    
    while (fread(&it, sizeof(struct item), 1, fp) == 1) {
        if (it.code == code) {
            printf("\nItem found:\n");
            printf("Code: %d\n", it.code);
            printf("Name: %s\n", it.name);
            printf("Quantity: %d\n", it.quantity);
            printf("Price: %.2f\n", it.price);
            printf("Value: %.2f\n", it.quantity * it.price);
            found = 1;
            break;
        }
    }
    
    if (!found)
        printf("Item with code %d not found\n", code);
    
    fclose(fp);
}

void update_quantity() {
    FILE *fp, *temp;
    struct item it;
    int code, qty, found = 0;
    
    printf("Enter item code to update: ");
    scanf("%d", &code);
    printf("Enter new quantity: ");
    scanf("%d", &qty);
    
    fp = fopen("inventory.dat", "rb");
    temp = fopen("temp.dat", "wb");
    
    if (fp == NULL || temp == NULL) {
        printf("File error\n");
        return;
    }
    
    while (fread(&it, sizeof(struct item), 1, fp) == 1) {
        if (it.code == code) {
            it.quantity = qty;
            found = 1;
        }
        fwrite(&it, sizeof(struct item), 1, temp);
    }
    
    fclose(fp);
    fclose(temp);
    
    remove("inventory.dat");
    rename("temp.dat", "inventory.dat");
    
    if (found)
        printf("Quantity updated successfully\n");
    else
        printf("Item not found\n");
}

int main() {
    int choice;
    
    while (1) {
        printf("\n=== INVENTORY MANAGEMENT SYSTEM ===\n");
        printf("1. Add Item\n");
        printf("2. Display All Items\n");
        printf("3. Search Item\n");
        printf("4. Update Quantity\n");
        printf("5. Exit\n");
        printf("Enter your choice: ");
        scanf("%d", &choice);
        
        switch (choice) {
            case 1: add_item(); break;
            case 2: display_all(); break;
            case 3: search_item(); break;
            case 4: update_quantity(); break;
            case 5: exit(0);
            default: printf("Invalid choice\n");
        }
    }
    
    return 0;
}