Final Project: Building a File-Based Database in C++

In this project, we’ll create a simple file-based database using file operations in C++. The goal is to implement basic database functionalities such as adding records, reading records, and searching for records from a file. This project will provide practical experience in working with files and help you better understand how to manage data using file-based storage.

Overview of the File-Based Database

A file-based database is a system where data is stored in files rather than in a more complex database system like MySQL or SQLite. In this simple version, we’ll create a database where records (such as user details or product information) are stored in a binary file, and we’ll implement basic CRUD (Create, Read, Update, Delete) operations using file handling.

For this example, let’s assume we are creating a contact management database where each record stores a person’s name, phone number, and email.


Step 1: Defining the Record Structure

Each record will consist of:

  • Name (string)
  • Phone Number (string)
  • Email Address (string)

We will store each record in a binary file to ensure efficiency and compactness. For simplicity, we will use a fixed-size structure to represent each record.

Defining the Record Structure in C++

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

// Define a structure for the database record
struct Contact {
    char name[100];
    char phoneNumber[15];
    char email[100];
    
    // Constructor to initialize the contact details
    Contact(const string& n = "", const string& p = "", const string& e = "") {
        strncpy(name, n.c_str(), sizeof(name) - 1);
        strncpy(phoneNumber, p.c_str(), sizeof(phoneNumber) - 1);
        strncpy(email, e.c_str(), sizeof(email) - 1);
    }
};

Step 2: Opening the File for Read/Write

We’ll open a binary file using the fstream class. The file will be used for both reading and writing, and we’ll use the binary mode (ios::binary) for efficient data storage.

Opening the Database File

fstream database("contacts.dat", ios::in | ios::out | ios::binary);

if (!database) {
    cout << "Error opening file!" << endl;
    return 1;
}

Step 3: Implementing Basic Database Functionalities

Now let’s implement the basic database operations: add, read, and search.


1. Adding a Record

To add a new contact, we will create a function that accepts contact details and writes them to the file.

void addContact(fstream& database, const Contact& newContact) {
    // Move the file pointer to the end of the file
    database.seekp(0, ios::end);
    // Write the contact record to the file
    database.write(reinterpret_cast<const char*>(&newContact), sizeof(Contact));
    cout << "Contact added successfully!" << endl;
}

In this function:

  • seekp(0, ios::end): Moves the write pointer to the end of the file, so new records are added at the end.
  • database.write(): Writes the contact data (in binary format) to the file.

2. Reading All Records

We’ll create a function that reads all records in the database and displays them.

void readContacts(fstream& database) {
    database.seekg(0, ios::beg);  // Move the read pointer to the beginning
    Contact tempContact;

    cout << "Contacts in the database: " << endl;
    while (database.read(reinterpret_cast<char*>(&tempContact), sizeof(Contact))) {
        cout << "Name: " << tempContact.name << ", Phone: " << tempContact.phoneNumber
             << ", Email: " << tempContact.email << endl;
    }
}

In this function:

  • seekg(0, ios::beg): Moves the read pointer to the beginning of the file.
  • database.read(): Reads a record from the file into the tempContact structure.

3. Searching for a Record

To search for a specific record, we’ll implement a function that accepts a name and searches for it in the file.

void searchContact(fstream& database, const string& name) {
    database.seekg(0, ios::beg);  // Move the read pointer to the beginning
    Contact tempContact;

    bool found = false;
    while (database.read(reinterpret_cast<char*>(&tempContact), sizeof(Contact))) {
        if (name == tempContact.name) {
            cout << "Contact found!" << endl;
            cout << "Name: " << tempContact.name << ", Phone: " << tempContact.phoneNumber
                 << ", Email: " << tempContact.email << endl;
            found = true;
            break;
        }
    }

    if (!found) {
        cout << "Contact not found." << endl;
    }
}

In this function:

  • We loop through each contact in the database file, comparing the name with the input. If a match is found, the details of that contact are displayed.

Step 4: Putting It All Together

Now, let’s create a simple menu-driven program that lets the user interact with the database to add, read, and search contacts.

int main() {
    fstream database("contacts.dat", ios::in | ios::out | ios::binary | ios::app);

    if (!database) {
        cout << "Error opening file!" << endl;
        return 1;
    }

    int choice;
    string name, phone, email;
    
    while (true) {
        cout << "1. Add Contact" << endl;
        cout << "2. View All Contacts" << endl;
        cout << "3. Search Contact" << endl;
        cout << "4. Exit" << endl;
        cout << "Enter your choice: ";
        cin >> choice;

        switch (choice) {
            case 1:
                // Add a new contact
                cout << "Enter name: ";
                cin.ignore();
                getline(cin, name);
                cout << "Enter phone number: ";
                getline(cin, phone);
                cout << "Enter email: ";
                getline(cin, email);
                
                addContact(database, Contact(name, phone, email));
                break;

            case 2:
                // View all contacts
                readContacts(database);
                break;

            case 3:
                // Search for a contact by name
                cout << "Enter name to search: ";
                cin.ignore();
                getline(cin, name);
                searchContact(database, name);
                break;

            case 4:
                // Exit the program
                database.close();
                return 0;
            
            default:
                cout << "Invalid choice. Please try again." << endl;
        }
    }
}

Step 5: Testing the Application

After implementing the database, test it by:

  • Adding multiple contacts to the database.
  • Viewing all contacts to check if the data is saved correctly.
  • Searching for a specific contact by name.

Step 6: Enhancements

Once the basic file-based database is working, you can enhance it by adding features such as:

  • Updating records: Modify existing contact records.
  • Deleting records: Remove a specific contact from the database.
  • Sorting records: Sort contacts alphabetically by name.
  • Implementing more advanced searching: Search by other attributes (phone number, email).

Conclusion

Building a file-based database in C++ gives you hands-on experience with file operations, structures, and the basics of data management. This project demonstrates how to:

  • Use binary files for efficient storage.
  • Perform basic CRUD operations (Add, Read, Search).
  • Implement record-based storage using structures.