File Handling in C: Sequential vs Random Access with Mini-Project Implementation for TU BCA

Author: Bhuban Subedi | Subject: C Programming (CACS151) | Semester: First Semester


In all our introductory C programming assignments, data lives strictly inside the computer’s volatile RAM. The moment your program terminates or the power goes off, all user inputs, calculated grades, and student scores vanish into thin air.

To create real-world software applications, we need data persistence—the ability to write information to permanent secondary storage (like an SSD or Hard Disk) and read it back at any time.

In the Tribhuvan University (TU) BCA First Semester C Programming board exams, File Handling carries guaranteed weightage (often a 7 to 10-mark long practical coding question). In this guide, we’ll master file opening modes, sequential vs random access (fseek, ftell, rewind), binary records (fread, fwrite), and build a complete student database mini-project.


1. The Basics of File Handling: The FILE Pointer

Every file operation in C is managed through a special data structure called FILE (defined inside <stdio.h>). It tracks the file’s current buffer, stream state, error flags, and current read/write cursor position.

FILE *fp;

The Universal File Handling Workflow:

+------------------+     +------------------+     +------------------+     +------------------+
| 1. Declare FILE  | --> | 2. Open Stream   | --> | 3. Read / Write  | --> | 4. Close Stream  |
|    Pointer (*fp) |     |    using fopen() |     |    Operations    |     |    using fclose()|
+------------------+     +------------------+     +------------------+     +------------------+

2. File Opening Modes in C

When opening a file using fopen(const char *filename, const char *mode), you must specify how you intend to interact with the file.

Mode Type Purpose If File Exists If File Does Not Exist
"r" Text Open for reading only Opens stream Returns NULL (Error)
"w" Text Open for writing Erases/Overwrites old content Creates a new file
"a" Text Open for appending Appends data to end Creates a new file
"r+" Text Open for both reading & writing Cursor at start Returns NULL
"w+" Text Open for reading & writing Overwrites old content Creates a new file
"a+" Text Open for reading & appending Reads from start, writes to end Creates a new file
"rb", "wb", "ab" Binary Binary file counterparts Exact same behavior as above, but in raw binary byte mode

Bhuban’s Warning: Beginners frequently make the fatal mistake of opening an existing file with "w" mode to add records. This immediately wipes all existing data! Always use "a" or "ab" mode when adding new records to an existing file.


3. Text Files vs Binary Files: Key Differences

In TU viva examinations, you will frequently be asked why we use binary files for storing structures instead of text files.

+------------------------------------+------------------------------------+
| Text Files (.txt)                  | Binary Files (.dat / .bin)         |
+------------------------------------+------------------------------------+
| Data is stored in human-readable   | Data is stored in raw binary bytes |
| ASCII / UTF-8 characters.          | (exact 0s and 1s as in RAM).       |
+------------------------------------+------------------------------------+
| Requires translation overhead for  | No translation overhead; fast read |
| numbers (e.g., float to string).   | and write operations.              |
+------------------------------------+------------------------------------+
| Uses `fprintf()`, `fscanf()`,      | Uses `fread()`, `fwrite()`.        |
| `fputs()`, `fgets()`.              |                                    |
+------------------------------------+------------------------------------+
| Larger file size for numbers.      | Compact and efficient storage.     |
+------------------------------------+------------------------------------+

4. Sequential vs Random File Access

Sequential Access:

In sequential file access, data is read or written sequentially from the beginning of the file to the end. To access the 50th record, you must read through the preceding 49 records first.

Random Access Functions:

Random access allows you to jump directly to any specific byte or record position without reading through preceding data.

  1. fseek(FILE *stream, long offset, int origin): Moves the file position cursor.
  2. origin can be:
    • SEEK_SET (0): Beginning of the file.
    • SEEK_CUR (1): Current cursor position.
    • SEEK_END (2): End of the file.
  3. ftell(FILE *stream): Returns the current byte offset of the cursor from the beginning of the file.
  4. rewind(FILE *stream): Instantly resets the file cursor back to the beginning (equivalent to fseek(stream, 0, SEEK_SET)).
File Bytes:  [0] [1] [2] [3] ... [40] [41] ... [End]
               ^                  ^              ^
               |                  |              |
           SEEK_SET            SEEK_CUR       SEEK_END

5. Master Lab Project: Student Record Management System

Here is a complete, well-structured, modular C program that stores student records in a binary file, allows searching by roll number, displays all records, and uses random access.

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

// Define Student Structure
struct Student {
    int rollNumber;
    char name[50];
    float gpa;
};

// Function prototypes
void addRecord(const char *filename);
void displayAll(const char *filename);
void searchRecord(const char *filename);

int main() {
    const char *filename = "tu_students.dat";
    int choice;

    while (1) {
        printf("\n========================================\n");
        printf("  TU BCA STUDENT RECORD MANAGEMENT (C)  \n");
        printf("========================================\n");
        printf("1. Add New Student Record\n");
        printf("2. Display All Student Records\n");
        printf("3. Search Student by Roll Number\n");
        printf("4. Exit\n");
        printf("----------------------------------------\n");
        printf("Enter your choice (1-4): ");

        if (scanf("%d", &choice) != 1) {
            printf("Invalid input! Exiting.\n");
            break;
        }

        switch (choice) {
            case 1:
                addRecord(filename);
                break;
            case 2:
                displayAll(filename);
                break;
            case 3:
                searchRecord(filename);
                break;
            case 4:
                printf("Thank you for using the system. Goodbye!\n");
                exit(0);
            default:
                printf("Invalid selection! Please enter 1, 2, 3, or 4.\n");
        }
    }

    return 0;
}

// Function to add a record in binary append mode
void addRecord(const char *filename) {
    FILE *fp = fopen(filename, "ab"); // "ab" = Append Binary
    if (fp == NULL) {
        printf("Error: Unable to open file for writing!\n");
        return;
    }

    struct Student s;
    printf("\nEnter Roll Number: ");
    scanf("%d", &s.rollNumber);
    printf("Enter Full Name: ");
    scanf(" %[^\n]s", s.name); // Read full line with spaces
    printf("Enter GPA (0.00 - 4.00): ");
    scanf("%f", &s.gpa);

    // Write structure block directly to file
    fwrite(&s, sizeof(struct Student), 1, fp);
    fclose(fp);

    printf(">> Record successfully saved to %s!\n", filename);
}

// Function to display all records sequentially
void displayAll(const char *filename) {
    FILE *fp = fopen(filename, "rb"); // "rb" = Read Binary
    if (fp == NULL) {
        printf("No records found or file does not exist.\n");
        return;
    }

    struct Student s;
    int count = 0;

    printf("\n%-10s %-30s %-10s\n", "Roll No", "Student Name", "GPA");
    printf("----------------------------------------------------\n");

    while (fread(&s, sizeof(struct Student), 1, fp) == 1) {
        printf("%-10d %-30s %-10.2f\n", s.rollNumber, s.name, s.gpa);
        count++;
    }

    printf("----------------------------------------------------\n");
    printf("Total Records Found: %d\n", count);

    fclose(fp);
}

// Function to search a record using random search
void searchRecord(const char *filename) {
    FILE *fp = fopen(filename, "rb");
    if (fp == NULL) {
        printf("No records found!\n");
        return;
    }

    int targetRoll, found = 0;
    struct Student s;

    printf("\nEnter Roll Number to Search: ");
    scanf("%d", &targetRoll);

    while (fread(&s, sizeof(struct Student), 1, fp) == 1) {
        if (s.rollNumber == targetRoll) {
            printf("\n>> RECORD FOUND! <<\n");
            printf("Roll Number: %d\n", s.rollNumber);
            printf("Name:        %s\n", s.name);
            printf("GPA:         %.2f\n", s.gpa);
            printf("Byte Offset: %ld bytes\n", ftell(fp) - sizeof(struct Student));
            found = 1;
            break;
        }
    }

    if (!found) {
        printf("No student found with Roll Number: %d\n", targetRoll);
    }

    fclose(fp);
}

6. Common Board Exam Questions & Solutions

Board Question:

“What is the difference between feof() and ferror() in C?” (TU BCA 5 Marks)

  • feof(fp): Checks whether the End-Of-File indicator has been set for the stream. It returns non-zero (true) only after an attempt has been made to read past the last byte of the file.
  • ferror(fp): Checks whether an I/O error (such as a corrupted disk block or hardware read failure) occurred during read/write operations. It returns non-zero if an error occurred.

Summary & Quick Revision Checklist

  1. Always verify file opening: if (fp == NULL) { /* handle error */ }.
  2. Always close streams: Call fclose(fp) to flush internal system write buffers to physical disk storage.
  3. Use Binary Mode (rb, wb, ab) for Structs: fwrite(&structVar, sizeof(structVar), 1, fp) writes the exact binary representation without ASCII conversion overhead.
  4. Master fseek parameters: fseek(fp, (n-1)*sizeof(record), SEEK_SET) directly jumps to the $n^{\text{th}}$ record.

LEAVE A REPLY

Please enter your comment!
Please enter your name here