Scrambling variables and inserting them into file c++ - c++

I am creating a c++ quiz maker which offers a user the ability to insert different types of questions and answers and it inserts all that info into a neat file. The whole code is here. My issue is that after gathering input to create a 'connect the word to definition' I do not know how I would scramble the variables of each word and definition to print out something like this:
1.) Connect the definitions to the word.
Astronaut An expert of automobiles.
Mechanic An individual that has been to space.
Pilot An individual who is knows about electronics.
Electronic engineer An individual who is skilled in flying planes.
void connectDefinitions() {
fstream file;
if (!file) {
cout << "Error when opening file.";
}
file.open("quiz.txt", ios::app);
if (file.is_open()) {
counter = 0;
file << current_numbered_question
<< ".) Connect the definitions to the word.";
file << "\n\n";
while (counter != 4) {
cout << "What is the word you would like to add?\n";
cin.ignore();
file << "\n\n";
getline(cin, user_word);
cout << "What is the definition of the word you added?\n\n";
cin.ignore();
file << "\n\n";
getline(cin, user_def);
if (counter == 0) {
connectword1 = user_word;
connectdef1 = user_def;
}
if (counter == 1) {
connectword2 = user_word;
connectdef2 = user_def;
}
if (counter == 2) {
connectword3 = user_word;
connectdef3 = user_def;
}
if (counter == 3) {
connectword4 = user_word;
connectdef4 = user_def;
}
counter++;
}
}
return;
}

Related

How to break from a while loop with bool? [duplicate]

This question already has answers here:
Read file line by line using ifstream in C++
(8 answers)
Closed 1 year ago.
I am trying to get the user to key in data and store them inside a vector so when -1 is being entered it will break the entire loop and start displaying the vector. However it is currently not working even if -1 has been entered the loop will still continue to go on.
void cargo::addCargo(vector<cargo> &userCargo)
{
fstream file;
file.open("cargo.txt", ios::out | ios::app);
int i = 0;
bool checker = true;
cargo newCargo;
while (checker == true)
{
cout << "Enter id" << endl;
if (cin.peek() == '-1')
{
checker = false;
cout << "Hello";
}
else
{
cin >> idCon;
newCargo.setId(idCon);
}
cout << "Enter Destination Country" << endl;
if (cin.peek() == '-1')
{
checker = false;
}
else
{
cin >> destCountryCon;
newCargo.setDestCountry(destCountryCon);
}
cout << "Enter Time" << endl;
if (cin.peek() == '-1')
{
checker = false;
}
else
{
cin >> timeCon;
newCargo.setTime(timeCon);
newCargo.setIndex(i + 1);
userCargo.push_back(newCargo);
}
cout << endl;
i++;
}
//loop to display the entire vector
displayCargo(userCargo);
}
You are code is not self-contained so I can't fix it for you. The pragmatic answer is break or continue with a flag like checker. peek() looks at the next character and when the user enters "-1" it will return '-' and not the multi-character constant '-1'. Alternative, user can press ctrl-d to trigger eof. If you want to use "-1" (who came up with that terrible ux?), read that as a string or int.

Searching through a text files for a certain word C++

I'm trying to create a searchable recipe database by ingredient for a project. I'm trying to create the for loop that goes through the string vector (which has each ingredient saved to it) and search through the file and compare them. Right now, I just want it to output "Hello!" if theres a match. With all my fiddling, theres either 100 Hello!s (definitely not right) or none. Here's the code:
int main()
{
int y;
cout << "Hello! Welcome to Abby's Recipe Calculator." << endl << endl;
cout << "Please select an option: 1 to search by ingredient or 2 to browse recipes..." << endl;
cin >> y;
vector <string> ingreds;
ingreds.reserve(4);
if (y == 1)
{
ingredientvector(ingreds); // calls function to fill vector w/ ingredients
}
//else if (y == 2)
//{
//call recipe function...
//}
Search x1(ingreds); //assigns ingredients to object vector
recipesearch(x1.getingreds());
system("pause");
return 0;
}
void ingredientvector(vector<string>& x)
{
cout << "SEARCH BY INGREDIENT" << endl;
cout << "Please enter up to three ingredients... " << endl;
for (int i = 0; i < 4; i++)
{
x.push_back(" ");
getline(cin, x[i]);
if (x[i] == "1")
{
break;
}
}
}
void recipesearch(const vector<string>& ingredientlist) //ifstream& recipes)
{
ifstream myrecipes;
string line;
string ingredient;
myrecipes.open("recipes.txt");
if (myrecipes.is_open())
{
for (int i = 0; i < 4; i++)
{
ingredient = ingredientlist[i];
while(getline(myrecipes, line)){
if (ingredient == line)
{
cout << "Hello!" << endl;
}
else
{
break;
}
}
}
}
else cout << "Unable to open recipe file!";
myrecipes.close();
}
Here is an example of a recipe used:
Cheese-y Ramen
Prep Time: 5 minutes
Cook Time: 20 minutes
Total Time: 25 minutes
Servings: 2
Ingredients:
8 oz cheddar cheese
1 tablespoon cornstarch
¾ cup milk
2 packages ramen noodles
Directions:
1. Grate cheddar cheese and add with cornstarch into a small bowl
2. Combine with milk in a medium saucepan and cook on medium to low heat until consistent. Keep warm until serving.
3. In a separate pan boil ramen noodles. Set aside the included flavor packets.
4. Once boiling, drain the noodles and combine with cheese.
Recipe from Buzzfeed
This reads the entire recipe file into a string, then looks inside the string for each ingredient. Note: This is extremely brute force. It's fast, but not going to be very accurate. For example, if Cheetos are mentioned in a side bar, not in the recipe itself, they will still be listed.
Credit where it's due, this answer lifts the file read wholesale from Read whole ASCII file into C++ std::string
void recipesearch(const vector<string>& ingredientlist)
{
ifstream myrecipes;
string file;
myrecipes.open("recipes.txt");
if (myrecipes.is_open())
{
// read entire file into string
myrecipes.seekg(0, std::ios::end);
file.reserve(myrecipes.tellg());
myrecipes.seekg(0, std::ios::beg);
file.assign((std::istreambuf_iterator<char>(myrecipes)),
std::istreambuf_iterator<char>());
// look inside file string for each ingredient
for (const string & ingredient: ingredientlist)
{
if (file.find(ingredient) != file.npos)
{ // found ingredient in file
cout << ingredient << endl;
}
}
}
else
cout << "Unable to open recipe file!";
}
Caveat: A lot of files you'll pull from the web are in encoded in a multi-byte character set to get prettier results and internationalization, not the 7 bit ASCII used by default by most of the standard C++ tools, including the those used in the above example code.
Correctly interpreting which of potentially many multi-byte character sets to use and how to consume them is a discussion topic unto itself, but for the purposes of this assignment OP may be able to get away with ensuring all input files are saved with ASCII encoding.
Try inverting the while and the for loop like such:
...the code before your for loop
while(getline(myrecipes, line))
{
for (int i = 0; i < 4; i++)
{
ingredient = ingredientlist[i];
if (ingredient == line)
{
cout << "Hello!" << endl;
}
else
{
break;
}
}
}
...the code after your for loop

Deleting content from a file [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I am making a program that allows a user to make bank accounts and save them too a file, you can also delete them. I am having issues with my code for deleting an account on the file, my function for deleting the account looks like this.
int deleteCustomer(account acc[], int numCust)
{
string target;
bool accFound = false;
int count = 0;
cout << "Enter account number: ";
cin >> target;
for (int i = 0; i < numCust; i++)
{
if (acc[i].acctNum == target)
{
accFound = true;
break;
}
count++;
}
if (accFound == false)
{
cout << "That account does not exist." << endl;
system("pause");
system("cls");
}
else if (accFound == true)
{
ofstream outFile;
outFile.open("customer.dat");
for (int i = count; i < numCust - 1; i++)
{
outFile << acc[i+1].acctNum;
outFile << '#';
outFile << acc[i+1].name;
outFile << '#';
outFile << acc[i+1].cBal;
outFile << '#';
outFile << acc[i+1].sBal;
outFile << '#';
}
numCust--;
outFile.close();
}
return numCust;
}
The function is supposed to overwrite the account selected by the user by saving the file ahead of it to the previous spot and return the new number of customers. It appears to run through fine but it does not do anything and I am uncertain as to why. Any input would be helpful, thank you.
Several problems here:
Your account lookup should be working, but you're overcomplicating this a bit (you'd only need one value rather than three, but let's skip that for now). If you're interested let me know.
You're never actually removing any account (just reducing the number of total accounts; which will then cause the last entry to be removed).
When saving you accounts to the file, you start at that selected index, which doesn't make any sense at all.
Let's assume you've got 10 accounts, indices 0 through 9.
The user picks the account at index 5.
You save accounts index 6 through 9(!) only.
The user picks the account at index 0.
You save accounts index 1 through 9 only.
Some style things:
You essentially store the selected account's index in count. That's fine, but very misleading. Don't ever use misleading variable names. As you might be able to tell from my comment above, I misread that part as well.
Rather than writing if (booleanValue == true) you could just write if (booleanValue), which results in the same code, but is shorter and might be faster to read. In a similar way, you could replace if (booleanValue == false) with !booleanValue.
Don't omit namespaces like std, if you can (e.g. use std::string rather than string and avoid using namespace std;) to avoid writing ambigious code. If some other namespace you use has string (or any other member) as well, you'll either have to explicitly name the namespace anyway or you're at least confusing others reading your code. Also there's always the potential bug introduced by unintentionally using a different type.
Fixing the actual problem:
I assume this is some homework assignment or some tutorial/class code or anything similar? If so, don't just copy the following code and instead try to think about how it's working. Once you understood, implement it yourself and only use my snippets if you're really stuck.
In general, it's good software design to keep code and functions minimal. Don't create "super functions" that do several things. Also try to make code reusable, so in case you change something, you're able to adjust it in one place only.
Take your code above for example. Whenever you add, delete, or update an account, you'll have to write the new file. Did you plan on replicating the same code multiple times? If you'd have to adjust your file format, you'd have to change it everywhere.
You'll also need some way to actually remove customer datasets. As you might be aware, deleteing entries in an array would require you to move all entries behind it (to keep it continguous). This can be a very expensive operation.
To avoid this, I'm adding a new member bool valid to account. By default, this is set to false. Once there's some data put there (either through reading from a file or by the user), it's value is set to true.
So instead split this into two separate functions (moving the common code - saving - to its own function):
// By returning an integer value, you're able to communicate issues or problems
// without having to rely on exceptions (in case you're using C++).
// Note that I don't do any error checking here for simplicity.
// Parameters:
// filename - the target file to write
// acc - the array holding all customer accounts
// size - the maximum amount of values in acc
// Return value: 0, if everything went fine
// (I skipped actual error handling to keep it simple!)
int saveCustomers(const char *filename, account acc[], int size) {
std::ofstream outFile(filename);
// Iterate over all entries
for (int i = 0; i < num; ++i) {
// Do we actually have to store the account?
if (acc[i].valid) {
outfile << acc[i].acctNum << '#' << acc[i].name; // write all the values the way you did
}
}
outFile.close();
return 0; // Everything ok
}
Now that this is done, you're able to create your functions to modify your customer data:
int deleteCustomerByNumber(account acc[], int num, std::string target) {
// Iterate over all accounts and look for the selected one
for (int i = 0; i < num; ++i) {
// Only check valid accounts and see whether it's the target
if (acc[i].valid && acc[i].acctNum == target) {
acc[i].valid = false; // Mark it as invalid
return 0; // Everything ok
}
}
return 1; // Didn't find it!
}
In a similar way you can look for empty/unused entries to actually write data to them.
Bonus - alternative (STL) approach:
Since you're using C++, I'd suggest you use a different data structure, not just a simple array:
If you use a STL container (more specific: a map), you're able to handle everything a lot easier.
#include <map>
// Create a typedef to simplify expressions
typedef std::map<std::string, account> accmap;
// All accounts would be stored in this object:
accmap accounts;
// To do a quick lookup of any account:
accmap::const_iterator a = accounts.find(accountNumber);
if (a == accounts.end())
;// Account not found!
else {
a->first; // This is your account number
a->second; // This is your `account` object
}
// To delete a specific account:
accounts.erase(accountNumber)
// To create a new account simply access it:
accounts[accountNumber].name = newName;
You need to save all of the records before the index and after the index, otherwise you are effectively deleting more than just the one account. Presumably you should also remove the record from the input array as well. You are also not doing any error handling on the input or output. And you need to fix your output loop, it is not using indexes correctly.
Try this:
int deleteCustomer(account acc[], int numCust)
{
string target;
int accFound = -1;
cout << "Enter account number: ";
if (cin >> target)
{
for (int i = 0; i < numCust; ++i)
{
if (acc[i].acctNum == target)
{
accFound = i;
break;
}
}
}
if (accFound == -1)
{
cout << "That account does not exist." << endl;
system("pause");
system("cls");
}
else
{
for (int i = accFound+1; i < numCust; ++i)
acc[i-1] = acc[i];
--numCust;
ofstream outFile;
outFile.open("customer.dat");
for (int i = 0; (i < numCust) && (outFile); ++i)
{
outFile << acc[i].acctNum;
outFile << '#';
outFile << acc[i].name;
outFile << '#';
outFile << acc[i].cBal;
outFile << '#';
outFile << acc[i].sBal;
outFile << '#';
}
if (!outFile)
cout << "Error saving customer file" << endl;
}
return numCust;
}
If you don't want to update the array, then you can do this instead:
int deleteCustomer(account acc[], int numCust)
{
string target;
int accFound = -1;
cout << "Enter account number: ";
if (cin >> target)
{
for (int i = 0; i < numCust; ++i)
{
if (acc[i].acctNum == target)
{
accFound = i;
break;
}
}
}
if (accFound == -1)
{
cout << "That account does not exist." << endl;
system("pause");
system("cls");
}
else
{
ofstream outFile;
outFile.open("customer.dat");
for (int i = 0; (i < numCust) && (outFile); ++i)
{
if (i != accFound)
{
outFile << acc[i].acctNum;
outFile << '#';
outFile << acc[i].name;
outFile << '#';
outFile << acc[i].cBal;
outFile << '#';
outFile << acc[i].sBal;
outFile << '#';
}
}
if (!outFile)
Cout << "Error saving customer file" << endl;
--numCust;
}
return numCust;
}
Lastly, when updating a file, it is a good idea to write the new data to a temp file first, then replace the original file with the temp file only if everything is successful. That way you reduce the risk of corrupting the original file.
To "deleting an account on the file", this part of code:
for (int i = count; i < numCust - 1; i++)
{
outFile << acc[i+1].acctNum;
outFile << '#';
outFile << acc[i+1].name;
outFile << '#';
outFile << acc[i+1].cBal;
outFile << '#';
outFile << acc[i+1].sBal;
outFile << '#';
}
should be
for (int i = 0; i < numCust; i++)
{
if(i == count) continue;// remove the account user selected
outFile << acc[i].acctNum;
outFile << '#';
outFile << acc[i].name;
outFile << '#';
outFile << acc[i].cBal;
outFile << '#';
outFile << acc[i].sBal;
outFile << '#';
}

File IO - delete a line of text that contains a specific string C++

I've written a function that reads in a text file, allows the user to pick an account to delete and deletes the specified account. What I need to do now is delete the line of text from the text file that lists the accounts on it. What good does it do to delete an account, but still have it show up in the list of accounts, right? Below is my code. What happens is that the entire contents of the account list text file get deleted, not just the line with the specific account number. The actual deletion of the account's text file works fine, it's just the deletion of that one line of text that is giving me trouble. Thanks for any assistance!
void UserInfo::deleteAccount() {
vector<string> accounts;
string line;
char answer;
ifstream acctList("accountList.txt");
if (acctList.fail()) {
cout << "There is a problem opening the file.\n";
exit(1);
}
//populate vector with the list of accounts and display them.
while (getline(acctList, line)) {
accounts.push_back(line);
}
for (unsigned int i = 0; i < (accounts.size()); i++) {
cout << accounts[i] << endl;
}
cout << "\nEnter the account number of the account you would like to delete: ";
cin >> acctNo;
cout << "Are you sure you want to delete account number " << acctNo << "? ";
cin >> answer;
const char * result = (acctNo + ".txt").c_str(); //convert the selection choice to a c-string
if (answer == 'y' || answer == 'Y') {
if (remove(result) != 0)
cout << "Unable to delete account." << endl;
else
cout << "\nThe account has been deleted successfully." << endl;
//delete the account name and number from the list of accounts
//temporary file to store the new list of accounts
ofstream out("newAcctList.txt", ios::app);
while (getline(acctList, line)) {
if (line != acctNo)
out << line << "\n";
acctList.close();
out.close();
// delete the original file
}
if(out){
remove("accountList.txt");
// rename old to new
rename("newAcctList.txt", "accountList.txt");
} else {
cout << "Error on output" << endl;
}
}//end if
You've already read acctList in the first while loop, so when you try to read it again to write out, it's already at the end. You can just iterate over the vector accounts - throwing away the entry matching acctNo (which should be a local variable BTW) - instead of re-reading the file.

Both If and Else condition execute C++ [duplicate]

This question already has answers here:
eof problem c++
(3 answers)
Closed 8 years ago.
I have a function for deleting data from a text file.
I'm having problems when valid input is entered:
if I enter bad input, it only executes the else part, as expected
if I enter valid input, it executes both the if and the else part.
Here is my code:
void Member::processTransaction()
{
fstream f;
f.open("Member.txt",ios::in|ios::out);
int x = 0, y = 0, z = 0;
while (!f.eof())
{
f >> idList[x] >> nameList[y];
if (id == idList[x])
{
cout << idList[x] << "\t" << nameList[x] << endl;
cout << "Do you want to delete the entry (Y/N) : " << endl;
char deleteEntry = getche();
if(deleteEntry=='Y'||deleteEntry=='y')
deleteInformation();
f.close();
}
else
{
cout << "No Matches Found!";
}
}
}
in the output. If I enter True, it executes and displays "No Matches Found".
If I enter false, it only displays "No Matches Found" and its fine.
while(!f.eof()){ is almost always a mistake. This case is no exception.
eof means you tried to read something previously, and it failed due to end of file. It's false if you have read the whole file exactly, and it's false if you close the file before trying to read past the end as you do in this example. And it's false if the stream is in an error state for another reason.
Instead , change to while (f >> idList[x] >> nameList[y]), and use break; if you want to exit the loop for some other reason than this read failing.
void Member::processTransaction() {
fstream f;
f.open("Member.txt", ios::in | ios::out);
int x = 0, y = 0, z = 0;
bool found = false; // found or not?
// search the file now.
while(!f.eof() && !found) {
f >> idList[x] >> nameList[y];
if(id != idList[x]) {
continue;
}
cout << idList[x] << "\t" << nameList[x] << endl;
cout << "Do you want to delete the entry (Y/N) : " << endl;
char deleteEntry = getche();
if(deleteEntry == 'Y' || deleteEntry == 'y') {
deleteInformation();
}
found = true;
}
f.close(); // close here
// not found only after you're done searching.
if(!found) {
cout << "No Matches Found!";
}
}
Your code is bad. My code is just less bad. Your entire way of doing this is flawed. But this is the right wrong way to do it.