Friday, November 3, 2023

Chapter 2: Contact Management in C | File Handling

 <<Previous Chapter

If you remember we have categoried all the functions into two types user functions and operational functions. Here I will explain the operational function and its usage in user functions one by one.

First, let's define Variables and Data Structure.


// Define Constant Variables
#define ESC 27
#define MAX_NAME_SZ 15
#define MAX_NUM_SZ 25
#define READ_ARRAY_SZ 15
#define DATA_FILE "dataFile.dat"
#define TEMP_FILE "tempFile.dat"

//Define data structure

struct data{
    char fName[MAX_NAME_SZ];
    char lName[MAX_NAME_SZ];
    char mPhone[MAX_NUM_SZ];
    char wPhone[MAX_NUM_SZ];
    char hPhone[MAX_NUM_SZ];
};
We will use struct data types to store contact information. It has only five fields First Name (fName), Last Name (lName), Mobile Phone (mPhone), Work Phone (wPhone), Home Phone (hPhone), and all these variables are char types. We can't set primary key as in database but we will choose Mobile Phone i.e mPhone as a primary key and will make sure that Mobile Phone number will not be a null value or duplicate while adding data into the file.

Function getString(): This function reads the data from the buffer and puts data into the array. You might think that scanf() function is very easy compared to this. Yes, you are true but there are problems with scanf() function. It does not clear the overflow value from the buffer. For example a user might input 20 long characters for the first name, but the size of the first name array is just 15 so remaining 5 characters will remain in the buffer. In the next use of scanf() function, it will read the previous remaining characters and you will get incorrect data. The scanf() function is useful only if you are sure about the input value.

//Get string input from user
static void getString(char *buff, size_t sz){

    fgets(buff,sz, stdin);
    if ((strlen(buff) > 0) && (buff[strlen (buff) - 1] == '\n'))
        buff[strlen (buff) - 1] = '\0';

    fflush(stdin);
}
Function readData(): This is a user function. It will interact with the user to get contact information. In the below code, you can see, it displays message using printf function and read input value using getString function which we have discussed above. After reading data, it will return the struct data type variable 'c'.

//Function to read contact data
struct data readData(){
    struct data c;

    gotoxy(35,11);
    printf("Enter first name: ");
    getString(c.fName,sizeof(c.fName));

    gotoxy(35,12);
    printf("Enter last name: ");
    getString(c.lName,sizeof(c.lName));

    gotoxy(35,13);
    printf("Enter mobile phone: ");
    getString(c.mPhone,sizeof(c.mPhone));

    gotoxy(35,14);
    printf("Enter work phone: ");
    getString(c.wPhone,sizeof(c.wPhone));

    gotoxy(35,15);
    printf("Enter home phone: ");
    getString(c.hPhone,sizeof(c.hPhone));

    return c;
} 
Declare File Pointers: Now let's declare the file pointers, and use these pointers to create files. Both pointers are required, you will find it in the code somewhere below.
//File pointers
FILE *fp, *ft;

Function add(): This is an operational function that adds the data into the file. Adding data into the file is very simple. First, open the file using file pointer fp in ab+ mode which is for both reading and appending data in binary mode. There are many opening modes in C, please google about it to study in detail. After opening the file, the fseek function will move the cursor always at the end of the file so that data will not get overridden. The function fwrite writes the data into the file in a block of size equal to the size of 5 char variables inside the struct.
At last, the file is closed using fclose function.

//Add data into the file
void add(struct data d){

    fp = fopen(DATA_FILE,"ab+");
    fseek(fp,0,SEEK_END);
    fwrite(&d,sizeof(d),1,fp);
    fclose(fp);
} 
Function searchByNum(): This function searches the numbers in the file from top to bottom one by one. If a number, passed in the function parameter is found then it will return 1 otherwise it will return 0.

//Search mobile number in the file
int searchByNum(char *num){

    struct data a;
    int i=0;
    if(strcmp(num,"")==0){
        i=2;
    } else {
        fp = fopen(DATA_FILE,"rb");
        while(fread(&a,sizeof(a),1,fp)==1){
            if(strcmp(num, a.mPhone)==0){
                i=1; //return 1 if found
            }
        }
        fclose(fp);
    }

    return i; //return 0 if not found
} 
Function addItems(): This function interacts with the user to get input data. After getting data, it checks the phone number in the file. If it exits will not add data and request the user to go back to update data. If it does not exist then it calls the add() function to save the data into the file that we had discussed above. At last, it allows the user to add another item or exit the function.  

//Add contacts
void addItems(){

    struct data c;

    system("cls");
    gotoxy(35,6);
    printf("----Welcome to Contact Management System----");
    gotoxy(35,8);
    printf("Please enter details:");

    c = readData();
    if(searchByNum(c.mPhone)==2){
        gotoxy(35,16);
        printf("Mobile number is empty. Record can't be saved.");
    } else if(searchByNum(c.mPhone)==1){
        gotoxy(35,16);
        printf("Mobile number %s exist. Please go back to update.",c.mPhone);
    } else {
        add(c);
        gotoxy(35,17);
        printf("Record Successfully Saved.");
    }
    gotoxy(35,27);
    printf("Please press any key to continue or enter ESC key to go back.");
    if(getch()==ESC){
        mainMenu();
    } else {
        addItems();
    }
} 
Function view(): This function reads the data from the file and stores the data into the array of structure data a.

//Read data from file
int view(struct data d[], int sz){

    struct data a;
    int i=0;
    fp = fopen(DATA_FILE,"rb");
    while(fread(&a,sizeof(a),1,fp)==1 && i<sz){
        d[i]=a;
        i++;
    }
    fclose(fp);
    return i;
}

Function viewItems(): This function gets the data from the view() function that we discussed above and displays the contact information in a tabular form. However, this function displays only 15 items only, due to the limitation of console windows size. You can implement a pagination feature to display the remaining items on the next page. I will add this feature in my next tutorials.  

//Display contacts only 15 items
void viewItems(){

    struct data c[READ_ARRAY_SZ];
    int data_sz=0;
    data_sz = view(c,READ_ARRAY_SZ);

    system("cls");
    gotoxy(35,6);
    printf("----Welcome to Contact Management System----");
    gotoxy(35,8);
    printf("View Contact function selected %d",data_sz);


    gotoxy(15,10);
    printf("First Name");
    gotoxy(35,10);
    printf("Last Name");
    gotoxy(55,10);
    printf("Mobile Number");
    gotoxy(70,10);
    printf("Work Number");
    gotoxy(85,10);
    printf("Home Number");
    for (int i=0;i
Function update(): To update the existing data, we first search for the old mobile number in the file. After finding the mobile number in the file it rewinds the file cursor just by 1 block of data using fseek function. You can check the logic below. After rewinding the cursor by 1 block, we will overwrite the data with the new value.

//Update data into the file
void update(struct data d, char *oldNumber){

    struct data a;

    fp=fopen(DATA_FILE,"rb+");

    while(fread(&a,sizeof(a),1,fp)==1){
        if(strcmp(oldNumber, a.mPhone)==0){
            fseek(fp,ftell(fp)-sizeof(a),0);
            fwrite(&d,sizeof(d),1,fp);
            fclose(fp);
        }
    }

} 
Function updateItems(): This function asks users to input the mobile number that they want to update. It searches the number in the file, and if found then it will ask the user for new contact information and pass the value into the update function which we have discussed above. If a number does not exist in the file, it simply requests the user to continue searching for another number or go back. 

//Update contact details
void updateItems(){

    char pNum[MAX_NUM_SZ];
    struct data c;
    system("cls");
    gotoxy(35,6);
    printf("----Welcome to Contact Management System----");
    gotoxy(35,7);
    printf("Please enter the Mobile Phone that you want to update details");
    gotoxy(35,9);
    printf("Enter Mobile Phone:");
    getString(pNum,sizeof(pNum));

    if(searchByNum(pNum)==1){
        gotoxy(35,10);
        printf("Mobile number %s exist. Please enter new details.",pNum);
        c=readData();
        update(c,pNum);
    } else {
        gotoxy(35,11);
        printf("Mobile number %s do not exist.",pNum);
    }
    gotoxy(35,27);
    printf("Please enter any key to continue or enter ESC key to go back.");
    if(getch()==ESC){
        mainMenu();
    } else {
        updateItems();
    }
} 
Function del(): This is the most interesting function among all the functions we created. I like the logic that we are using to delete files. Actually, we don't have any function to delete data from the file directly because once we allocate a block of data in a file it is not possible to remove that block of data. So in the below function, we copy all the data from the source file to the target file except the data that we want to delete. It is more like shifting memory blocks from one file to another file. After copying all the data we delete the source file and rename the target file as the source file name. 
However, this technique is not efficient if the data size is huge. For huge data file, we have to find different techniques that I will leave with you for now.

//Delete data from the file
void del(char *delNumber){

    struct data a;

    fp=fopen(DATA_FILE,"rb+");
    ft=fopen(TEMP_FILE,"wb+");

    while(fread(&a,sizeof(a),1,fp)==1){
        if(strcmp(delNumber, a.mPhone)!=0){
            fwrite(&a,sizeof(a),1,ft);
        }
    }

    fclose(ft);
    fclose(fp);

    remove(DATA_FILE);
    rename(TEMP_FILE,DATA_FILE);

} 
Function deleteItems(): This function requests the mobile phone number that the user wants to delete. It searches the data in the file if it exists then it deletes the data by calling del() function, if not it requests the user to continue for another number or go back.

//Delete contacts details
void deleteItems(){

    char pNum[MAX_NUM_SZ];
    char choice;
    system("cls");
    gotoxy(35,6);
    printf("----Welcome to Contact Management System----");
    gotoxy(35,7);
    printf("Please enter the Mobile Phone that you want to delete");
    gotoxy(35,9);
    printf("Enter Mobile Phone:");
    getString(pNum,sizeof(pNum));

    if(searchByNum(pNum)==1){
        gotoxy(35,12);
        printf("Mobile number %s exist. Do you want to delete it?.",pNum);
        gotoxy(35,14);
        printf("Please press y to delete or any key to skip.");
        choice=tolower(getch());
        if(choice=='y'){
            del(pNum);
            gotoxy(35,16);
            printf("Phone number successfully deleted.");
        }

    } else {
        gotoxy(35,12);
        printf("Mobile number %s do not exist.",pNum);
    }

    gotoxy(35,27);
    printf("Please enter any key to continue or enter ESC key to go back.");

    if(getch()==ESC){
        mainMenu();
    } else {
        deleteItems();
    }
}
This is the end of the code. I hope you have understood the logic implemented in the application. Please go back to the project's main page to download source code Project Main Page.

You can also watch a video about this project on YouTube.


No comments:

Post a Comment