C++ Arrays and Reading from file - c++

I'm creating an address book program that allow user to search by first name, last name,phone number and address. The user is prompted to enter a file name and the file is read into an array. I'm having trouble modifying by existing SearchFirstName function to loop through array. I have read over this topic multiple times I'm just not understanding it. Any help would be greatly appreciated.
File
Susan, Smith, 123 456 789
101 Main Street
Bob, Smith, 567 345 9076
456 Market Street
Header File
#include<string>
using namespace std;
enum Title {Mr, Mrs, Ms, Dr, NA};
struct NameType {
Title title;
string firstName;
string lastName;
};
struct AddressType {
string street;
string city;
string state;
string zip;
};
struct PhoneType {
int areaCode;
int prefix;
int number;
};
struct entryType {
NameType name;
AddressType address;
PhoneType phone;
};
const int MAX_RECORDS = 50;
struct addressBookType {
entryType record[MAX_RECORDS];
int numEntries;
};
Code
string bookArray[MAX_RECORDS];
int main()
{
entryType userRecord;
string filename;
ifstream inData;
char searchOption;
OpenFile(filename, inData);
MainMenu(inData, filename);
return 0;
}
void OpenFile(string& filename, ifstream& inData)
{
do {
cout << "Enter file name to open: ";
cin >> filename;
inData.open(filename.c_str());
if (!inData)
cout << "File not found!" << endl;
} while (!inData);
if(inData.is_open())
{
for(int i=0; i<MAX_RECORDS;i++)
{
inData>> bookArray[i];
}
}
}
// Searches passed file stream for a first name read from the user
void SearchFirstName(ifstream& inData)
{
string searchName;
entryType userRecord;
string normalSearchName, normalFirstName;
char choice;
bool found = false;
cout << "Enter first name to search for: ";
cin >> searchName;
normalSearchName = NormalizeString(searchName); // Convert name to all uppercase
// Loop through all records in the file
while (GetRecord(inData, userRecord)){
normalFirstName = NormalizeString(userRecord.name.firstName); // Convert retrieved string to all uppercase
if (normalFirstName == normalSearchName) { // Requested name matches
PrintRecord(userRecord);
cout << "Is this the correct entry? (Y/N)";
cin >> choice;
choice = toupper(choice);
cout << endl;
if (choice == 'Y') {
found = true;
break;
}
}
}
// Matching name was found before the end of the file
if (inData && !found){
cout << "Record found: " << endl;
PrintRecord(userRecord);
cout << endl;
}
else if (!found) // End of file. Name not found.
{
cout << searchName << " not found!" << endl << endl;
}
// Clear file fail state and return to beginning
inData.clear();
inData.seekg(0);
}
My attempt
void SearchFirstName(ifstream& inData)
{
string searchName;
entryType userRecord;
cout << "Enter first name to search for: ";
cin >> searchName;
string newSearchName = NormalizeString(searchName);
string upFirst = NormalizeString(userRecord.name.firstName);
for (int i=0;i<MAX_RECORDS;i++)
{
while(newSearchName == upFirst)
{
if (bookArray[i]== upFirst)
{
cout<<"Name Found";
cout <<bookArray[i]; //test case
}
}
}
}

Create your array, in this case it'll be an std::vector because that is more simple to use, by running your GetRecord function inside of a while loop and appending the result to the vector w/vector_variable_name.push_back(NormalizeString(value_returned_from_GetRecord));. The NormalizeString part is so you don't have to call it billions of times later.
Pass in your array like so void SearchFirstName(std::vector<entryType> *in_data_arr>)
Change your while loop to a for loop: for (int i = 0; i < in_data_arr.size(); i++) {
Inside the loop change normalSearchName = NormalizeString(searchName); to normalSearchName = in_data_arr[i].name.firstName;
And from there it should generally be identical.

Related

When I try to use cin in my C++ program, it throws a strange exception

So in the program I'm currently writing, the user is supposed to add people to a vector by inputting their names and partisan identities. However, I have been unable to make the code that actually stores the input work. The program first prompts the user for a name; then, once the user gives a name, it prompts the user again for a partisan identity. Whenever I enter more than one word for the name (e.g. "John Smith"), instead of accepting the input, the program throws this exception.
It also gives this error when I enter "D" or "R" in response to the second prompt, no matter how I respond to the first prompt. Does anyone have an idea what I'm doing wrong here? Here's the code I've written so far:
#include "DelibDem.h"
#include <stdio.h>
#include <vector>
//initializing variables
using namespace std;
bool continue_ = true;
string name = "";
string partyID = "";
int numD = 0;
int numR = 0;
int difference = 0;
int vectorSize = 0;
int newVectorSize = 0;
struct person{
string Name;
string PartyID;
string equivalentName;
string equivalenceClass;
};
vector<person> Sample;
int main()
{
//user adds people to the vector. I have not yet implemented the code that actually adds the people specified by the user yet, because I am still trying
//to figure out why my cin code is not working.
while (continue_ == true) {
string personName;
string personPartyID;
cout << "Enter a person's name: ";
cin >> personName;
cout << "Enter the person's party ID (D or R): ";
cin >> personPartyID;
if (personPartyID == "D") struct person inputtedPerson = { personName, personPartyID, NULL, "Republicans" };
else struct person inputtedPerson = { personName, personPartyID, NULL, "Democrats" };
cout << "Do you wish to add more people? (Y/N) ";
string answer;
cin >> answer;
if (answer == "N") continue_ = false;
}
//The number of Democrats in the sample is stored in numD. The number of Republicans is stored in numR.
for (auto& element : Sample)
{
if (element.PartyID == "D") numD++;
else numR++;
}
//print the number of Democrats and Republicans
cout << numD;
cout << numR;
//print the properties of each element in the sample
for (auto& element : Sample)
{
cout << element.Name << endl;
cout << element.PartyID << endl;
cout << element.equivalentName << endl;
cout << element.equivalenceClass << endl;
cout << "\n";
}
return 0;
}

How can I read a document and write to it at the same time

struct employee
{
string empid;
string lastn;
string firstn;
float hours;
float payrate;
float taxrate;
};
const int SIZE = 100;
int main()
{
employee eCount[SIZE];
string empF;
int count, option1;
fstream inFile;
inFile.open("personnel1.dat", ios::in | ios::out);
count = 0;
cout << "Enter an Employee ID to Change Values (i.e Cv299): ";
cin >> empF;
cin.ignore();
while (!inFile.eof())
{
count += 1;
inFile >> eCount[count].empid >> eCount[count].lastn >> eCount[count].firstn >> eCount[count].hours >> eCount[count].payrate >> eCount[count].taxrate;
if (empF == eCount[count].empid)
{
do
{
cout << "1. Change Employee's ID (" << eCount[count].empid << ")\n";
cout << "2. Change Employee's Last Name (" << eCount[count].lastn << ")\n";
cout << "3. Change Employee's First Name (" << eCount[count].firstn << ")\n";
cout << "4. Change Employee's Hours Worked (" << eCount[count].hours << ")\n";
cout << "5. Change Employee's Pay Rate (" << eCount[count].payrate << ")\n";
cout << "6. Change Employee's Tax Rate (" << eCount[count].taxrate << ")\n";
cout << "7. Quit!\n\n";
cout << "Enter 1 - 7: ";
cin >> option1;
cin.ignore();
switch (option1)
{
case 1:
{
cout << "What would you like to change the Employee ID to?: ";
getline(cin, eCount[count].empid);
break;
}
case 2:
{
cout << "What would you like to change the Employee's Last Name to?: ";
getline(cin, eCount[count].lastn);
break;
}
case 3:
{
cout << "What would you like to change the Employee's First Name to?: ";
getline(cin, eCount[count].firstn);
break;
}
case 4:
{
cout << "What would you like to change the Employee's Hours Worked to?: ";
cin >> eCount[count].hours;
cin.ignore();
break;
}
case 5:
{
cout << "What would you like to change the Employee's Pay Rate to?: ";
cin >> eCount[count].payrate;
cin.ignore();
break;
}
case 6:
{
cout << "What would you like to change the Employee's Tax Rate to?: ";
cin >> eCount[count].taxrate;
cin.ignore();
break;
}
default:
break;
}
} while (option1 != 7);
}
}
inFile << eCount[count].empid << eCount[count].lastn << eCount[count].firstn << eCount[count].hours << eCount[count].payrate << eCount[count].taxrate;
inFile.close();
}
The program is supposed to read the file and also allow the user to write to it. After inputting a valid employee ID it pops up the menu allowing the user to enter what they want to change. After inputting those changes and closing the file, it doesn't update the file. It still shows the same values from before.
You can do some thing like,
This code copies Line by line from old file to a tempfile. When you find the line which you want to update, change it with your line and copy it to the tempfile. Then finally copy all data from tempfile to file and delete tempfile
#include<iostream>
#include<fstream>
#include<stdlib.h>
using std::cout;
using std::endl;
using std::cin;
using std::ios;
class Employee
{
public:
std::string empname;
std::string empid;
};
int main()
{
std::fstream file("emp"),tempfile("tempemp");
Employee emp;
std::string id;
if(!file.is_open()||!tempfile.is_open())
{
file.open("emp",ios::out); //create a file
tempfile.open("tempemp",ios::out); //create a file
if(!file.is_open()||!tempfile.is_open())
{
cout<<"File Error";
exit(1);
}
else
{
file.close();
tempfile.close();
file.open("emp"); // open file for both read and write
tempfile.open("emp"); // open file for both read and write
if(!file.is_open()||!tempfile.is_open())
{
cout<<"File Error";
exit(1);
}
}
}
for(int i=0;i<3;i++) // taking 3 employee name and id
{
cout<<"Enter Name and id:"<<endl;
cin>>emp.empname;
cin>>emp.empid;
file.write((char*)&emp,sizeof emp);
}
cout<<"Contents:"<<endl; //printing contents
file.seekg(0,ios::beg); //move get pointer to beginning
while(file.peek()!=EOF)
{
file.read((char*)&emp,sizeof emp);
cout<<emp.empname<<endl;
cout<<emp.empid<<endl;
}
file.clear(); //reset file
cout<<"Enter Id to Modify:"<<endl;
cin>>id; // which id to modify
file.seekg(0,ios::beg); //move get pointer to beg
tempfile.seekp(0,ios::beg); //move get pointer to beg
while(file.peek()!=EOF)
{
file.read((char*)&emp,sizeof emp);
if(emp.empid==id) //if emp id in file equal to id then
{ //save to tempfile the name and id of user entered
cout<<"Enter New name and id:"<<endl;
cin>>emp.empname;
cin>>emp.empid;
tempfile.write((char*)&emp,sizeof emp);
}
else // if not equal save the content in file to tempfile
{
tempfile.write((char*)&emp,sizeof emp);
}
}
file.clear(); //reset file
tempfile.seekg(0,ios::beg); //move get pointer pos to beg
file.seekp(0,ios::beg); //move put pointer pos to beg
while(tempfile.peek()!=EOF)
{
tempfile.read((char*)&emp,sizeof emp); //copy contents of tempfile to file
file.write((char*)&emp,sizeof emp);
}
file.clear(); //reset file
tempfile.close(); //close file
remove("tempemp"); //remove tempemp pointed by tempfile
cout<<"Modified Contents:"<<endl; //print modified contents
file.seekg(0,ios::beg); //move get pointer pos to beg
while(file.peek()!=EOF)
{
file.read((char*)&emp,sizeof emp);
cout<<emp.empname<<endl;
cout<<emp.empid<<endl;
}
file.close(); //close file
return 0;
}
i am used name and id only for simplicity
(OR)
you can use like this also,But this code did not use tempfile. it replaces the new content directly in the file.
#include<iostream>
#include<fstream>
#include<stdlib.h>
using std::cout;
using std::endl;
using std::cin;
using std::ios;
class Employee
{
public:
std::string empname;
std::string empid;
};
int main()
{
std::fstream file("emp");
Employee emp;
std::string id;
std::fstream::pos_type pos=0; //for position
if(!file.is_open())
{
file.open("emp",ios::out);
if(!file.is_open())
{
cout<<"File Error";
exit(1);
}
else
{
file.close();
file.open("emp");
if(!file.is_open())
{
cout<<"File Error";
exit(1);
}
}
}
for(int i=0;i<3;i++)
{
cout<<"Enter Name and id:"<<endl;
cin>>emp.empname;
cin>>emp.empid;
file.write((char*)&emp,sizeof emp);
}
cout<<"Contents:"<<endl;
file.seekg(0,ios::beg);
while(file.peek()!=EOF)
{
file.read((char*)&emp,sizeof emp);
cout<<emp.empname<<endl;
cout<<emp.empid<<endl;
}
file.clear();
cout<<"Enter Id to Modify:"<<endl;
cin>>id;
file.seekg(0,ios::beg);
file.seekp(0,ios::beg);
pos=file.tellp(); //get init pos
while(file.peek()!=EOF)
{
pos=file.tellp(); //update pos
file.read((char*)&emp,sizeof emp);
if(emp.empid==id)
{
cout<<"Enter New name and id:"<<endl;
cin>>emp.empname;
cin>>emp.empid;
file.seekp(pos); // change pos
file.write((char*)&emp,sizeof emp); //save modified content
}
}
file.clear();
cout<<"Modified Contents:"<<endl;
file.seekg(0,ios::beg);
while(file.peek()!=EOF)
{
file.read((char*)&emp,sizeof emp);
cout<<emp.empname<<endl;
cout<<emp.empid<<endl;
}
file.close();
return 0;
}
In file streams only tellg changes tellp but it not for other streams for more info see this
are "seekp" & "seekg" interchangeable?

c++ error: 'std::string' has no member

I am creating a directory program that prompts a user for a file name and reads the file into a string array. I'm have trouble in my SearchFirstName function. I get an error:'std::string' has no member named 'userRecord'. I'm not sure how to fix this because userRecord is declared.
Header
#include<string>
using namespace std;
enum Title {Mr, Mrs, Ms, Dr, NA};
struct NameType {
Title title;
string firstName;
string lastName;
};
struct AddressType {
string street;
string city;
string state;
string zip;
};
struct PhoneType {
int areaCode;
int prefix;
int number;
};
struct entryType {
NameType name;
AddressType address;
PhoneType phone;
};
const int MAX_RECORDS = 50;
Code
// string bookArray[MAX_RECORDS];
entryType bookArray[MAX_RECORDS]; //Solution
int bookCount = 0;
void OpenFile(string& filename, ifstream& inData)
{
do {
cout << "Enter file name to open: ";
cin >> filename;
inData.open(filename.c_str());
if (!inData)
cout << "File not found!" << endl;
} while (!inData);
if(inData.is_open())
{
for(int i=0; i<MAX_RECORDS;i++)
{
inData>> bookArray[bookCount];
++bookCount;
}
}
}
void SearchFirstName(ifstream& inData)
{
entryType userRecord; // Declaration of userRecord
string searchName;
string normalSearchName, normalFirstName;
char choice;
bool found = false;
cout << "Enter first name to search for: ";
cin >> searchName;
for(int i = 0; i < bookCount; ++i){
normalFirstName = NormalizeString(bookArray[i].userRecord.name.firstName);
// Convert retrieved string to all uppercase
if (normalFirstName == normalSearchName) { // Requested name matches
PrintRecord(bookArray[i].userRecord.name.firstName);
cout << "Is this the correct entry? (Y/N)";
cin >> choice;
choice = toupper(choice);
cout << endl;
if (choice == 'Y') {
found = true;
break;
}
}
}
// Matching name was found before the end of the file
if (inData && !found){
cout << "Record found: " << endl;
PrintRecord(userRecord);
cout << endl;
}
else if (!found) // End of file. Name not found.
{
cout << searchName << " not found!" << endl << endl;
}
// Clear file fail state and return to beginning
inData.clear();
inData.seekg(0);
}
string bookArray[MAX_RECORDS];
bookArray is of type string.It should be
entryType bookArray[MAX_RECORDS];
Also
normalFirstName = NormalizeString(bookArray[i].userRecord.name.firstName);
bookArray[i] cannot have userRecord as a member.userRecord is variable that you have declared.
It should be
normalFirstName = NormalizeString(bookArray[i].name.firstName);

Extra string outputs if user input is more than one word

This is my first time asking a question on here, so be gentle lol. I wrote up some code for an assignment designed to take information from a (library.txt datatbase) file, store it in arrays, then access/search those arrays by title/author then output that information for the user based on what the user enters.
The issue I am having is, whenever the user enters in a search term longer than one word, the output of "Enter Q to (Q)uit, Search (A)uthor, Search (T)itle, (S)how All: " is repeated several times before closing.
I am just looking to make this worthy of my professor lol. Please help me.
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;
struct Book
{
string title;
string author;
};
int loadData(string pathname);
char switchoutput();
void showAll(int count);
int showBooksByAuthor(int count, string name);
int showBooksByTitle(int count, string title);
int FindAuthor(int count, string userinput);
int FindTitle(int count, string userinput);
void ConvertStringToLowerCase(const string orig, string& lwr); //I found this program useful to convert any given string to lowercase, regardless of user input
const int ARRAY_SIZE = 1000;
Book books[ARRAY_SIZE];
int main()
{
string pathname;
string name;
string booktitle;
int count = 0;
int counta = 0;
int countt = 0;
char input = 0;
cout << "Welcome to Jacob's Library Database." << endl;
cout << "Please enter the name of the backup file: " ;
cin >> pathname;
count = loadData(pathname);
cout << count << " records found in the database." << endl;
while (toupper(input != 'Q'))
{
input = switchoutput(); // function call for switchoutput function
switch (input)
{
case 'A':
cout << "Author's Name: ";
cin >> name;
counta = showBooksByAuthor(count, name);
cout << counta << " records found." << endl;
break;
case 'T':
cout << "Book Title: ";
cin >> booktitle;
countt = showBooksByTitle(count, booktitle);
cout << countt << " records found." << endl;
break;
case 'S':
showAll(count);
break;
case 'Q':
break;
}
}
//Pause and exit
cout << endl << "Press 'ENTER' to quit";
getchar();
getchar();
return 0;
}
int loadData(string pathname) //loading data into the array of structs
{
ifstream inFile;
inFile.open(pathname);
if (!inFile) {
cout << "Error, could not read into file. Please re-compile." << endl;
system("PAUSE");
exit(1); //if not in file, exit;
}
int i = 0;
while (!inFile.eof()) {
getline(inFile, books[i].title);
getline(inFile, books[i].author);
i++;
}
return i;
}
char switchoutput() //seperate output function to get my characteroutput constantly resetting and returning the uppercase version for my switch
{
char input;
cout << "Enter Q to (Q)uit, Search (A)uthor, Search (T)itle, (S)how All: ";
cin >> input;
return toupper(input);
}
int showBooksByAuthor(int count, string name)
{
int authorcount = 0;
authorcount = FindAuthor(count, name);
return authorcount;
}
int showBooksByTitle(int count, string title)
{
int titlecount = 0;
titlecount = FindTitle(count, title);
return titlecount;
}
void showAll(int count)
{
for (int i = 0; i < count; i++)
{
cout << books[i].title << " (" << books[i].author << ")" << endl;
}
}
int FindAuthor(int count, string userinput)
{
int authorcount = 0;
string stringlower, arraylower;
int num;
// called upon function to lowercase any of the user inputs
ConvertStringToLowerCase(userinput, stringlower);
for (int i = 0; i < count; ++i) //this function's count determines at which locations to output the author and names (an argument from books by author)
{
// called upon function to lowercase any of the stored authors'
ConvertStringToLowerCase(books[i].author, arraylower);
num = arraylower.find(stringlower); // searches string for userinput (in the lowered array) and stores its value
if (num > -1) // you can never get a -1 input value from an array, thus this loop continues until execution
{
cout << books[i].title << " (" << books[i].author << ")" << endl; //cout book title and book author
authorcount++; //count
}
}
return authorcount;
}
int FindTitle(int count, string userinput) //same as previous but for titles
{
int titlecount = 0;
string stringlower, arraylower;
int num;
ConvertStringToLowerCase(userinput, stringlower);
for (int i = 0; i < count; ++i)
{
ConvertStringToLowerCase(books[i].title, arraylower);
num = arraylower.find(stringlower);
if (num > -1)
{
cout << books[i].title << " (" << books[i].author << ")" << endl;
titlecount++; //count
}
}
return titlecount;
}
void ConvertStringToLowerCase(const string orig, string& lwr) // I found this from another classmate during tutoring, I thought to be useful.
{
lwr = orig;
for (int j = 0; j < orig.length(); ++j) //when called upon in my find functions, it takes the string and convers the string into an array of lowercase letters
{
lwr[j] = tolower(orig.at(j));
}
}

Deleting a record from a file in c++

I have wriiten a code to add , delete and dispaly a record of employees consisting of employee ID ,name,age and location.
But I am unable to code the delete function
My code is as follows:
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
class Document
{
public:
int Add();
void Delete();
void Display();
int No_of_lines();
int empno();
private:
fstream document;
fstream newdocument;
string data;
int age;
int emp_id;
int idtodelete;
char name[100],loc[100];
};
int Document::No_of_lines()
{
int number = 0;
document.open("document.txt");
while (getline(document, data))
{
++number;
}
return number;
}
int Document::Add()
{
Document d1;
document.open ( "document.txt",ios::app);
int x = d1.No_of_lines();
int emp_id = ++x;
cout << "The employee ID is: " << emp_id;
document <<emp_id;
cout<< "\n Ënter Name:" ;
cin>>name;
document <<"\t Name:"<< name;
cout<<"Enter Age:";
cin>> age;
document << "\t Age:"<< age;
cout<< "Enter location:";
cin>> loc;
document << "\t Location:"<< loc;
document << "\n";
document.close();
return 0;
}
void Document::Delete()
{
Document d2;
d2.Display();
int num;
char line[1000];
document.open("document.txt");
newdocument.open("newdocument.txt");
cout << "Enter the ID to delete \n";
cin >> idtodelete;
while (document.good())
{
const int maxchar = 1000;
const int maxtokens = 10;
char* token[maxtokens] = {};
char split[maxchar];
document.getline(split, maxchar);
int n = 0;
token[0] = strtok(split, " ");
istringstream(token[0]) >> num;
if (num != idtodelete)
{
document >> emp_id >> name >> age >> loc;
newdocument << emp_id<< name<< age<< loc;
}
else
{
}
}
document.close();
newdocument.close();
remove("document.txt");
rename("newdocument.txt", "document.txt");
}
void Document::Display()
{
document.open("document.txt");
while (!document.eof())
{
getline(document,data);
cout<<data<<endl;
}
document.close();
}
int main()
{
Document d;
char ans;
int ch;
do
{
system ( "cls");
cout<< "Enter your choice \n";
cout << "\t1. Add Data \n " << "\t2. Delete Data \n" << "\t3. Display Data \n";
cout<< "\t4. Exit\n";
cout<< " Enter Choice \n ";
cin >> ch;
switch(ch)
{
case 1:
cout << " Adding Data : \n";
d.Add();
break;
case 2:
//cout << "Deleting data : \n";
d.Delete();
break;
case 3:
cout << "Displaying data : \n";
d.Display();
break;
case 4:
cout << "Exit";
break;
default :
cout << "Invalid Input \n";
break;
}
cout<< " click y to quit or any other key to continue " ;
cin>>ans;
}
while (ans != 'y');
return 0;
}
The simple way is to remove by employee ID. You just ask for the employee ID, to know what employee to remove.
Then, you cannot remove lines in the middle of a sequential file, so you just
rename the file as document.back
create a new document.txt
read document.back and copy all employees to document.txt except the one you want to delete
close both files
remove document.back
That's all ... except for the usual test for IO errors, backup file existing, and so on...
I tested your code. First, you forgot to close document in method int Document::No_of_lines(). Next on my MSVC2008, I have to explicitely call document.clear() after reaching end of file. You also do not test document immediately after a getline, meaning that you execute the code after a bad read.
I removed newdocument from Document class, because IMHO it is useless. Here is a possible implementation of Delete:
void Document::Delete()
{
Document d2;
Display();
int num;
document.open("document.txt");
document.clear();
d2.document.open("newdocument.txt", ios::out | ios::trunc);
cout << "Enter the ID to delete \n";
cin >> idtodelete;
while (document.good())
{
getline(document, data);
if (document) {
int n = 0;
istringstream(data) >> num;
if (num != idtodelete)
{
d2.document << data << endl;
}
}
}
document.close();
d2.document.close();
remove("document.txt");
rename("newdocument.txt", "document.txt");
}