Phone DB app c++

Pages: 12
Hi All

I have developed a phone DB application with user name, address, contact number related info. Also created functions for adding, viewing, searching, editing and deleting those contacts from the data base. How can I fetch contacts in alphabet order , sorting by first name, then sorting by last name? Also need to fetch names based on search string provided by the user. Right now in my code if i search for name by providing full string, only then it displays that name. I would rather need that name to be displayed based on search string. For example if the Phone DB has a name Tenzin John and if I type only the first or last full name, it returns the name. But if I search by string like Te or Jo, nothing is displayed. Please provide your inputs.
Below is the code so far, I need to find a way to sort it by names based on alphabet order and then make the phone application to work as expected using C++.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
using namespace std;
// Function prototypes
void addPhoneContacts();
void viewPhoneContacts();
void searchPhoneContact();
void editPhoneContact();
void deletePhoneContact();

int main(){	//Main Function
system("cls");
bool run=true;
do{
int Option;   			//Main menu
	cout << "----------------------Phone Book-----------------------------" << endl;
	cout << "\n";
	cout << "What do you need to perform here?" << endl;
	cout << "1.) Add Phone Contact" << endl;
	cout << "2.) Edit Phone Contact" << endl;
	cout << "3.) Delete Phone Contact" << endl;
	cout << "4.) View All Phone Contacts" << endl;
	cout << "5.) Search Phone DB" << endl;
	cout << "6.) Exit" << endl << endl;
	cout << "Select an option: ";
	cin >> Option;
	cin.ignore();
switch (Option){
	case 1:
		addPhoneContacts();
		break;
	case 2:
		editPhoneContact();
		break;
	case 3:
		deletePhoneContact();
		break;
	case 4:
		viewPhoneContacts();
		break;
	case 5:
		searchPhoneContact();
		break;
	case 6:
		run = false;
		break;
	default:
		cout << "Select between options 1 to 6" <<endl;
		int main();
}
} while(run);
cout << "Program got Terminated";
}

void addPhoneContacts(){		//Function is for adding the phone contacts.
	system ("cls");
	string Firstname, Lastname, Address, Contact, list, name, Firstname2, Lastname2, Address2, Contact2;
	double counter, number;
	cout << "----------------------Phone DB-----------------------------" << endl << endl;
	cout << "Enter 'quit' at First name to quit" << endl << endl;
	cout << "Enter First Name: ";
	getline(cin, Firstname);
	if (Firstname == "quit")
	main();
	cout << "Enter Last Name: ";
	getline(cin, Lastname);
	cout << "Enter Address: ";
	getline(cin, Address);
	cout << "Enter Contact Number: ";
	getline(cin, Contact);
	
	ifstream asd("PhoneDB.txt");
	while(asd >> counter >> Firstname2 >> Lastname2 >> Address2 >> Contact2){
		if (counter == 500){
			cout << "Invalid as the Max number of contacts has been reached already (500).";
			main ();
		}
		else number = counter;
	}
	ofstream adb("PhoneDB.txt", ios::app);
	number = number + 1;
	adb << number << " " << Firstname << " " << Lastname
	<< " " << Address << " " << Contact << endl;
	system("pause");
	system("cls");
}

void viewPhoneContacts(){	//Show all the entries stored in the data base.
	system("cls");
	double counter;
	string Firstname, Lastname, Address, Contact;
	ifstream phonedb("PhoneDB.txt");
	cout << "Entry #" << setw(17) << "First Name" << setw(23)<< "Last Name" << setw(23) <<"Address"<< setw(29)<<"Contact"<< endl << endl;
	while (phonedb >> counter >> Firstname >> Lastname >> Address >> Contact){
		cout << setw(3)<< counter << setw(18)<< Firstname << setw(25) << Lastname << setw(25) << Address << setw(30) << Contact << endl;
	}
	cout << endl;
	system ("pause");
	system ("cls");
}

void searchPhoneContact(){	//Allow to specific the entry here.
	system("cls");
	int choice;
	double counter, number;
	string Firstname, Lastname, Address, Contact, Firstname2, Lastname2, Address2, Contact2;
	
	cout << "----------------------Phone DB-----------------------------" << endl << endl;
	cout << "---Search Phone DB---" << endl;
	cout << "1.) First name" << endl;
	cout << "2.) Last name" << endl;
	cout << "3.) Address" << endl;
	cout << "4.) Contact " << endl;
	cout << "Enter Choice: ";
	cin >> choice;
	switch (choice){
		case 1:
			cout << "Enter First Name: ";
			cin >> Firstname;
			cout << endl;
			break;
		case 2:
			cout << "Enter Last Name: ";
			cin >> Lastname;
			cout << endl;
			break;
		case 3:
			cout << "Enter Address: ";
			cin >> Address;
			cout << endl;
			break;
		case 4:
			cout << "Enter Contact: ";
			cin >> Contact;
			cout << endl;
			break;
		default:
			cout << "Please Enter choice from 1 to 4";
			searchPhoneContact();
	}
	ifstream search("PhoneDB.txt");
	if (choice==1){
		while (search >> counter >> Firstname2 >> Lastname2>> Address2 >> Contact2){
			if(Firstname == Firstname2){
				cout << counter << " " << Firstname2 << " " << Lastname2 << " " << Address2 << " " << Contact2 << endl << endl;
			}
		}
	}
	if (choice==2){
		while (search >> counter >> Firstname2 >> Lastname2>> Address2 >> Contact2){
			if(Lastname == Lastname2){
				cout << counter << " " << Firstname2 << " " << Lastname2 << " " << Address2 << " " << Contact2 << endl << endl;
			}
		}
	}
	if (choice==3){
		while (search >> counter >> Firstname2 >> Lastname2>> Address2 >> Contact2){
			if(Address == Address2){
				cout << counter << " " << Firstname2 << " " << Lastname2 << " " << Address2 << " " << Contact2 << endl <<endl;
			}
		}
	}
	if (choice==4){
		while (search >> counter >> Firstname2 >> Lastname2>> Address2 >> Contact2){
			if(Contact == Contact2){
				cout << counter << " " << Firstname2 << " " << Lastname2 << " " << Address2 << " " << Contact2 << endl << endl;
			}
		}
	}
	system ("pause");
	system ("cls");
}



Last edited on
Below is the remaining code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

void editPhoneContact(){		//To edit the entries.
	system("cls");
	int choice;
	double counter, number;
	string Firstname, Lastname, Address, Contact, Firstname2, Lastname2, Address2, Contact2, choice2, choice3;
	ifstream edit("PhoneDB.txt");
	ofstream temp("Temp.txt", ios::app);
	cout << " Provide Entry number that you wish to edit: ";
	cin >> choice;
	cout << endl;
	if (choice==0 || choice > 500){
		cout << "Error, wrong entry";
		system("pause>0");
		editPhoneContact();
	}
	while (edit >> counter >> Firstname2 >> Lastname2>> Address2 >> Contact2){
			if (counter==choice){
				cout << counter << " " << Firstname2 << " "<< Lastname2 << " " << Address2 << " " << Contact2 << endl<<endl;
				cout << "Is this the contact that you wish to edit? (y or n) ";
				cin >> choice3;
				cout <<endl;
			}
		
			if (choice3=="n") {
				main();
				}
			if (choice3=="y"){
			if (counter<choice){
				temp << counter << " " << Firstname2 << " "<< Lastname2 << " " << Address2 << " " << Contact2 << endl;
			}
			if (counter==choice){
				cout << "Enter New First name: ";
				cin >> Firstname;
				cout << "Enter New Last name: ";
				cin >> Lastname;
				cout << "Enter New Address: ";
				cin >> Address;
				cout << "Enter New Contact: ";
				cin >> Contact;	
				
				temp << choice << " " << Firstname << " "<< Lastname << " " << Address << " " << Contact << endl;
			}
				if (counter > choice){
					temp << counter << " " << Firstname2 << " "<< Lastname2 << " " << Address2 << " " << Contact2 << endl;
				}
			}
		}
edit.close();
temp.close();
if (remove("PhoneDB.txt")==0){
	cout << "Successfully Removed the File" << endl;
}else{
	cout << "Error removing the file"<< endl;
}
if(rename("Temp.txt", "PhoneDB.txt")==0){
	cout << "Successfully Renamed the file"<< endl;	
}else{
	cout << "Error renaming the file"<<endl;
}
system("pause");
system("cls");
}

void deletePhoneContact(){	//To delete entries one by one.
		system("cls");
	int choice;
	double counter, number;
	string Firstname, Lastname, Address, Contact, Firstname2, Lastname2, Address2, Contact2, choice2,choice3;
	ifstream edit("PhoneDB.txt");
	ofstream temp("Temp.txt", ios::app);
	cout << "Provide the Entry number that you wish to delete: ";
	cin >> choice;
	cout << endl;
	while (edit >> counter >> Firstname2 >> Lastname2>> Address2 >> Contact2){
		if (counter==choice){
				cout << counter << " " << Firstname2 << " "<< Lastname2 << " " << Address2 << " " << Contact2 << endl<<endl;
				cout << "Is this the contact that you wish to delete? (y or n) ";
				cin >> choice3;
				cout << endl;
		}
		if (choice3=="n") {
			main();
		}
		if (counter<choice){
			temp << counter << " " << Firstname2 << " "<< Lastname2 << " " << Address2 << " " << Contact2 << endl;
		}
		if (counter > choice){
			temp << counter - 1 << " " << Firstname2 << " "<< Lastname2 << " " << Address2 << " " << Contact2 << endl;
		}
	}

edit.close();
temp.close();
if (remove("PhoneDB.txt")==0){
	cout << "Succesfully Removed the File" << endl;
}else{
	cout << "Error removing the file"<< endl;
}
if(rename("Temp.txt", "PhoneDB.txt")==0){
	cout << "Succesfully Renamed the file"<< endl;	
}else{
	cout << "Error renaming the file"<<endl;
}
system("pause");
system("cls");
}
if you mean first name, last name as in
bob ablah
bob jones //jones comes after ablah?

then you do that with your own comparison function:
bool compare(string &fn1, string &ln1, string &fn2, string &ln2)
{
return (fn1 < fn2) || (fn1==fn2 && ln1 < ln2);
}
and use that to sort by. just replace the standard comparison with your own in the sort.

selection and bubble sort both are N*N. Neither one is good for much if you care about speed. If you don't care about speed, they are equally bad.
if you are unable or unwilling to use the built in std::sort, then you can use shell sort, which is similar in code to selection sort, with a small twist that makes it much, much faster. There is a similar trick for bubble sort called the comb sort.
so flip a coin if you don't care, if you have time, branch out into these modifications to get a huge performance lift and learn something cool -- both of these are fascinating algorithms, though neither are the 'best' they are still worth a study. The best is currently intro sort, which has recursion, helper functions, fallback to a simpler sort like shell or comb, and more ... its difficult to code correctly for a beginner.
Last edited on
Right now in my code if I search for name by providing full string, only then it displays that name. I would rather need that name to be displayed based on search string. For example if the Phone DB has a name "Tenzin John" and if I type only the first or last full name, it returns the name. But if I search by string like "Te" or "Jo" matching strings, nothing gets displayed. This string matching must not be case sensitive. How can I do this? Do you mean selection or bubble sort has performance issues? Is hashing and unordered_map a good option here? How can I implement a faster sort based approach here?
Last edited on
searching for partial values is complicated. how good are you wanting that? If you allow for typos, you can get into a big mess with near keys on a keyboard (detect which type of keyboard layout is in use..) or a list of common typos / phonetic misspellings in the given languages ... it gets ugly fast!

you can do a substring search, and blasphemy as it may be, strstr is pretty darn good at it.
that is simply
match is true if
strstr(a,b) //a is user type in, b is stored data)
is nonzero.
that uses C style strings, though.
or you can use std::find on C++ strings which is similar but not a boolean result, you can look up how to use it.
Don't forget to normalize all the letters of the input and target case so you match A to a and so on.

yes, selection and bubble sort are exceedingly slow if you have a lot of data. Modern computers are extremely fast, so you need a fair bit of data to notice this as a human. That 20 records took 10 nanoseconds instead of 1 isnt really something that stands out to people :)


hashing is a different idea. Hashing gets your data back instantly, but you cannot easily get it back in any kind of sorted order. If you don't need the data sorted, even the fastest sorts do a lot of work: don't sort unless you need to! If you don't need it sorted, unordered map is an excellent tool.

while on the subject, if you need data sorted a bunch of ways, or if the data is really fat, you can sort a vector of pointers to the data so that your swapping is just an integer (the pointer) changing positions, and not all that data. So if you want last name first sorted by last name as an option and first name, last sorted by first name as an option... making a quick side container of pointers to the data and resorting THAT is often the way to deal with it.
Last edited on
I need to fetch names based on search string provided by the user. So if user provides string like "Te" or "Jo" matching strings, it must display the matching names in the DB. But in my case it doesnt display those matching strings, rather need to provide complete first or last names to fetch the info. Do you suggest to use vector and list here?
Last edited on
Instead of if(Firstname == Firstname2 you would need sth. like

if(FirstName.find(FirstName2) != std::string::npos)

https://en.cppreference.com/w/cpp/string/basic_string/find

This is case-sensitive. If you don't want that you have to convert both string first to uppercase or lowercase.

Another thing to consider is splitting your search function into smaller functions like searchForFirstName, searchForLastname...
> I would rather need that name to be displayed based on search string.
> This string matching must not be case sensitive.

One option is to use a case insensitive regular expression.
https://en.cppreference.com/w/cpp/regex

For instance:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <string>
#include <regex>

// return true if text contains fragment
bool contains( const std::string& text, const std::string& fragment )
{
    // std::regex::icase : ignore case for matching characters
    // we assume that fragment does not contain special characters
    // (if it does, we would need to add escape sequences for characters in fragment)
    return std::regex_match( text, std::regex( ".*" + fragment + ".*",  std::regex::icase ) ) ;
}

int main()
{
    const std::string full_name =  "Tenzin John" ;
    std::cout << "text: '" << full_name << "'\n" << std::boolalpha ;
    for( std::string fragment : { "TENz", "in jOH", "EnzI", "zing" } )
        std::cout << "contains '" << fragment << "'? " << contains( full_name, fragment ) << '\n' ;
}

http://coliru.stacked-crooked.com/a/ff05acbaa977999b


> How can I implement a faster sort based approach here?

Use a sort provided by the standard library.
https://en.cppreference.com/w/cpp/algorithm/ranges/sort
https://en.cppreference.com/w/cpp/algorithm/sort

For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <string>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <ranges>

struct info
{
    std::string first_name ;
    std::string last_name ;
    std::string address ;
    std::string contact ;

    auto operator <=> ( const info& ) const noexcept = default ;

    friend std::ostream& operator<< ( std::ostream& stm, const info& inf )
    {
        static const auto sw = std::setw(10) ;
        return stm << sw << inf.first_name << sw << inf.last_name << sw << inf.address << sw << inf.contact ;
    }
};

int main()
{
    std::vector<info> abook { { "mnop", "qrst", "3456", "4444" },
                              { "abcd", "wxyz", "4567", "9999" },
                              { "mnop", "aabb", "1211", "2222" },
                              { "abcd", "efgh", "1234", "8888" }
                            } ;
    const auto print_abook = [&] { for( const info& inf : abook ) std::cout << inf << '\n' ; std::cout << "--------\n" ; } ;
    print_abook() ;

    // sort on first name, then last_name and print
    std::ranges::sort(abook) ;
    print_abook() ;

    // sort on last_name and print
    std::ranges::sort( abook, {}, &info::last_name ) ;
    print_abook() ;

    // sort on contact and print
    std::ranges::sort( abook, {}, &info::contact ) ;
    print_abook() ;
}

http://coliru.stacked-crooked.com/a/ff05acbaa977999b
I do not need string matching to be case sensitive here. Do you mean to write separate functions for searching first and last names? How can I sort them separately?
In the first post L66 main(). You shouldn't recursively call main(). It is called by the run-time and shouldn't be called again in the program.

Also, you are working all the time on the contents of the file. For each operation you open the file, perform the operation and then the file is closed ready for the next.

I would be considering reading the whole file once into a suitable container at the start of the program, perform all the required operations on the container and then when exit the menu write the current contents of the container back to the file. This makes such operation as delete much easier. And operations such as search much easier.

If you use a std::map for a container, then it is automatically sorted by the specified key. If you use a std::vector then you can use std::sort() to sort it.

For a partial search, do you want the specified search criteria to match any part of first or last name or just the start? So if the stored name is foo bar and the search criteria is oo, then does this match foo? Does search for r match bar? Or doesn't match either?

PS If you want to do 'matches' that can include 'incorrect' spelling then you could consider using something like soundex for matching.
Last edited on
While doing partial search for those DB entries whose first or last name as it is stored in DB or partially match the provided string search, from the beginning of the name.
Then using .find() you're check that the return value would be .begin(). If it's not .begin() then it isn't found at the beginning of the name.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>
#include <regex>
#include <algorithm>

// return true if text starts with fragment, ignoring case
bool starts_with_icase( const std::string& text, const std::string& fragment ) 
{ return std::regex_match( text, std::regex( fragment + ".*", std::regex::icase ) ) ; }

const auto equal_to_icase = []( unsigned char a, unsigned char b )
{ return std::toupper(a) == std::toupper(b) ; } ;

// return true if text starts with fragment, ignoring case
bool starts_with_icase_2( const std::string& text, const std::string& fragment )
{ return !std::ranges::search( text, fragment, equal_to_icase ).empty() ; }
starts_with_icase_2() seems to find the fragment if anywhere in the string:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <algorithm>
#include <string>
#include <cctype>
#include <array>
#include <iostream>
#include <iomanip>

const auto equal_to_icase = [](unsigned char a, unsigned char b) { return std::toupper(a) == std::toupper(b); };

// return true if text starts with fragment, ignoring case
bool starts_with_icase_2(const std::string& text, const std::string& fragment) {
	return !std::ranges::search(text, fragment, equal_to_icase).empty();
}

int main() {
	constexpr std::array words { "abd", "bdc", "bab" };

	for (const auto& w : words)
		std::cout << "bd" << "  " << w << "  " << std::boolalpha << starts_with_icase_2(w, "bd") << '\n';

	for (const auto& w : words)
		std::cout << "ab" << "  " << w << "  " << std::boolalpha << starts_with_icase_2(w, "ab") << '\n';
}



bd  abd  true
bd  bdc  true
bd  bab  false
ab  abd  true
ab  bdc  false
ab  bab  true


Shouldn't it be:

1
2
3
4
5
6
// return true if text starts with fragment, ignoring case
bool starts_with_icase_2(const std::string& text, const std::string& fragment) {
	const auto ret { std::ranges::search(text, fragment, equal_to_icase) };

	return !ret.empty() && ret.begin() == text.begin();
}



bd  abd  false
bd  bdc  true
bd  bab  false
ab  abd  true
ab  bdc  false
ab  bab  false

Last edited on
> Shouldn't it be:

Yes. Thanks!
I guess the ranges library is only in C++20. Is there any alternative to that in C++11 or C++14? I am trying to find an alternate way to sort names.
You don't need ranges for sorting or searching - although they can be helpful. As I mentioned in my post above, I would approach this application by first reading the file into a container then perform operations on the container as per the menu, then when finished write the container's content back to the file. To sort say a std::vector then you use std::sort. If you use a std::map, this is automatically stored sorted by the key. If you have a sorted container, then you can use std::binary_search() to search - otherwise find/search can be used.

As happens with a lot of the additions to the C++ standard the Boost libraries were there first.

https://www.boost.org/doc/libs/1_79_0/libs/range/doc/html/index.html

Usable with C++03 or later.

Whether Boost::Range will fit your needs only you can decide. There is a sort and stable_sort algorithm in the library.

Boost is a nice way to see what is likely to become a part of the C++ stdlib in the future.
C++11:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <string>
#include <iomanip>
#include <vector>
#include <algorithm>

struct info
{
    std::string first_name ;
    std::string last_name ;
    std::string address ;
    std::string contact ;

    // auto operator <=> ( const info& ) const noexcept = default ;

    friend std::ostream& operator<< ( std::ostream& stm, const info& inf )
    {
        static const auto sw = std::setw(10) ;
        return stm << sw << inf.first_name << sw << inf.last_name << sw << inf.address << sw << inf.contact ;
    }
};

int main()
{
    std::vector<info> abook { { "mnop", "qrst", "3456", "4444" },
                              { "abcd", "wxyz", "4567", "9999" },
                              { "mnop", "aabb", "1211", "2222" },
                              { "abcd", "efgh", "1234", "8888" }
                            } ;
    const auto print_abook = [&] { for( const info& inf : abook ) std::cout << inf << '\n' ; std::cout << "--------\n" ; } ;
    print_abook() ;

    // sort on first name and print
    // sort with a predicate: see https://cplusplus.com/articles/NhA0RXSz/ 
    std::sort( abook.begin(), abook.end(), [] ( const info& a, const info& b ) { return a.first_name < b.first_name ; }  ) ;
    print_abook() ;

    // sort on last_name and print
    std::sort( abook.begin(), abook.end(), [] ( const info& a, const info& b ) { return a.last_name < b.last_name ; }  ) ;
    print_abook() ;

    // sort on contact and print
    std::sort( abook.begin(), abook.end(), [] ( const info& a, const info& b ) { return a.contact < b.contact ; }  ) ;
    print_abook() ;
}

http://coliru.stacked-crooked.com/a/ce75613e40699b41
... but sorting is much easier in C++20 with ranges:

1
2
3
4
5
std::ranges::sort(abook, {}, &info::first_name);

std::ranges::sort(abook, {}, &info::last_name);

std::ranges::sort(abook, {}, &info::contact);


Pages: 12