Hi guys im working on a school project which is an rpg game. I have created a load_game function which is supposed to go through a specific file, check if the file has the id the user enters and load that specific characters stats into global variables.
The problem is that I made a function to print out the content of the file so the users can see their options but it doesnt print anything at all, I made a new_player function which fills the same file with new players and that works correctly. When i change a file to another one and write into that file manually it works. Here is the code:
void load_game()
{
bool loading = true;
do{
ifstream in_stream("Players.txt");
ofstream out_stream("Players.txt");
cout << " The last son of Allheaven " << endl;
cout << "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+" << endl;
char ch;
in_stream.clear();
in_stream.seekg(0, in_stream.beg);
while (in_stream.get(ch))
{
cout << ch;
}
cout << " Enter -1 to return to main menu " << endl;
cout << "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+" << endl;
cout << endl << "Enter a number to access one of the above options: ";
in_stream.clear();
in_stream.seekg(0, in_stream.beg);
int choice;
cin >> choice;
while (in_stream >> ID >> Name >> Class >> HP >> Mana >> ATK >> ability_dmg >> Defense >> magic_resist >> player_exp >> player_level)
{
if (choice == ID)
{
/*game_start();*/
loading = false;
break;
}
else if (choice == -1)
{
system("cls");
main_menu();
}
else if (in_stream.eof())
{
in_stream.clear();
in_stream.seekg(0, in_stream.beg);
cout << endl << "Error! Invalid ID !" << endl;
}
}
in_stream.close();
out_stream.close();
} while (loading);
}
A major problem is that you open the same file twice. Once for input, and once for output which will truncate the file if it exists. So when you create out_stream you will effectively remove all the data in the file.
If you want to read from and write to the same file either use std::iofstream to open it in read/write mode. With text files it's hard to edit data in the file though, so I rather recommend you open a temporary file for the output, and when done you rename the temporary file as the actual file.
Or in your case, why have the output file at all? You don't actually use it anywhere? All it does is truncating the file you want to read.
Related
Problem Sample Run
Problem Description (1)
Problem Description (2)
The link above is an image of what the program should do once reading in a text file and outputting it correctly to another file. The two other links are descriptions of the problem itself. My problem is that while most of the logic works, when it comes to printing it out, it skips the first number in the input file. For example, if the input file was:
1 10000
2 5000
3 150000
Right now the output in the file prints:
Store 2: *
Store 3: ***
Again, the image provides a better example of what is to happen. The code should work for any text file given, no matter for the order of the numbers: (ex: 50 10000, 5 5000, so on).
I am not sure why this happening. I'm attaching my code for reference below. I would like to apologize in advance for the lack of comments right now, I'm trying to fix the error first. I have narrowed the error down to the while loop in the main function however. Second, I'm somewhat of a beginner, so please excuse any silly mistakes I've made or if I did things in a more inefficient way. Another note is that I can't change the signatures for the functions, and I have to check the values if they are valid in the readFile() function. I also cannot use arrays, or the pause command, or break and continue. Third, I am pretty new to stack overflow so please do excuse any errors I make. Thank you!
As of now, the code that is commented out, is code I don't plan to use, but if there is a way to achieve the goal using that code and staying within the guidelines, please do let me know. This error is quite a frustrating one! Also, I do have some more error to fix afterwards, but those are minor ones I can fix later. I want to fix this error first. Thank you!
#include <iostream>
#include <iomanip>
#include <cmath>
#include <fstream>
using namespace std;
bool readFile(ifstream&, long long int&, unsigned int&);
void display(ofstream&, long long int, unsigned int);
int main()
{
ifstream inputFile;
ofstream outputFile;
string fileName;
long long int salesData;
unsigned int storeNumber;
cout << "Enter input file name" << endl;
cin >> fileName;
inputFile.open(fileName);
bool fileRead = readFile(inputFile, salesData, storeNumber);
if(fileRead)//inputFile >> storeNumber >> salesData)
{
outputFile.open("saleschart.txt");
outputFile << "SALES BAR CHART" << endl;
outputFile << "(Each * equals 5,000 dollars)" << endl;
while(inputFile >> storeNumber >> salesData)
{
display(outputFile, salesData, storeNumber);
/*
if(storeNumber < 1 || storeNumber > 99)
{
cout << "The store number " << storeNumber << " is not valid" << endl;
}
if(salesData < 0)
{
cout << "The sales value for store " << storeNumber << " is negative" << endl;
}
*/
}
inputFile.close();
outputFile.close();
}
return 0;
/*
while(inputFile >> storeNumber >> salesData)
{
int counter = 1;
for(int i = 1; i <= counter; i++)
{
counter++;
bool fileRead = readFile(inputFile, salesData, storeNumber);
if(fileRead)
{
outputFile.open("saleschart.txt");
outputFile << "SALES BAR CHART" << endl;
outputFile << "(Each * equals 5,000 dollars)" << endl;
display(outputFile, salesData, storeNumber);
}
}
*/
}
bool readFile(ifstream& inputFile, long long int& salesData, unsigned int& storeNumber)
{
if(inputFile)
{
inputFile >> storeNumber >> salesData;
if(storeNumber == NULL)
{
cout << "The file was empty" << endl;
return false;
}
if(storeNumber < 1 || storeNumber > 99)
cout << "The store number " << storeNumber << " is not valid" << endl;
if(salesData < 0)
cout << "The sales value for store " << storeNumber << " is negative" << endl;
else
return true;
}
else
{
cout << "File \"sales.txt\" could not be opened" << endl;
return false;
}
return false;
/*
if(inputFile.eof())
return false;
else
{
inputFile >> storeNumber >> salesData;
return true;
}
*/
}
void display(ofstream& outputFile, long long int salesData, unsigned int storeNumber)
{
outputFile << left << setw(6) << "Store" << right << setw(2) << storeNumber << ": ";
cout<<storeNumber; //DEBUG
for(int i = 0; i < (salesData/5000); i++)
{
outputFile << left << "*";
}
outputFile << endl;
}
It's skipping the first numbers because you read them (and the don't save them) in the function readFile. It's got nothing to do with your while loop which is completely correct. But you can't read the same numbers twice, and you have already read the first numbers by the time you get to your while loop.
Not sure what you are expecting from the function readFile, it looks like you tried to read the file in a separate function but then abandoned it. If you just delete the readFile function your code should work.
OK reading your question again I see that are required to use the readFile function. If that is the case then the correct thing to do is delete the current contents of the readFile function and move the while loop into the readFile function.
You have a few problems, the main is that the readFile function reads the first two values, and then you discard the data it has read.
This discarded data will never be written to the output file.
Also in the readFile function you have the comparison storeNumber == NULL which might be a check if the input failed, but that's not how to do that.
First of all because C++ doesn't have null values, NULL is an old C-compatibility constant for a null pointer.
Secondly, you already have the correct check in the loop where you read the remaining data, where you use the whole input expression inputFile >> storeNumber >> salesData as the condition.
Now to put it all together, you don't need the readFile function at all, instead all you need is the reading loop:
outputFile.open("saleschart.txt");
if (!outputFile)
{
// Failed to open the output file
return 1;
}
inputFile.open(fileName);
while(inputFile >> storeNumber >> salesData)
{
display(outputFile, salesData, storeNumber);
}
Disclaimer: I am a beginner to programming, so what I say might sound really stupid
I have to make a "Telephone Directory" for school. The program isn't complete, but there are some things that I need to fix before moving on. The array TelephoneNumbers either isn't storing the numbers from the file correctly, or isn't displaying them. For the SeaerchRecords function, the first number in the file is displayed correctly, the second is displayed as "2147483647," and the rest of the numbers display as "0." The modify function also doesn't change the number, and I confirmed this with the while in the function. The string array works perfectly fine, however. May someone explain what I'm doing incorrectly?
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
string TelephoneNames[100];
int TelephoneNumbers[100];
void ModifyRecords(); //Function to Modify Records
void SearchRecords(); //Function to Search Records
void DeleteRecords(); //Function to Delete Records
int main()
{
fstream inputFile;
fstream outputFile;
char choice;
inputFile.open("Telephone Names.txt"); //To store
for (int count=0;count<100;count++) //file names
{ //into a
inputFile >> TelephoneNames[count]; //string
}
inputFile.close();
inputFile.open("Telephone Numbers.txt");//To store
for (int count=0;count<100;count++) //file #'s
{ //into a
inputFile >> TelephoneNumbers[count];//string
}
inputFile.close();
//Display options available
cout << " Hello, do you want to:\n";
cout << " ======================\n";
cout << "-Modify Records|Enter M\n";
cout << "-Search Records|Enter S\n";
cout << "-Delete Records|Enter D\n";
//Store choice
cin >> choice;
//Send to different function
if (choice=='M'||choice=='m')
{
ModifyRecords();
}
if (choice=='S'||choice=='s')
{
SearchRecords();
}
return 0;
}
void ModifyRecords()
{
string name;
string newname;
int newnumber;
int count=0;
cout << "Enter the name of the person: ";
cin >> name;
for (count=0;TelephoneNames[count]!=name;count++)//To determine where in the strings the new numbers need to be
{
}
cout << "Enter the new name of the person: ";
cin >> newname;
cout << "Enter the new number of the person: ";
cin >> newnumber;
TelephoneNames[count]={newname};
TelephoneNumbers[count]={newnumber};
count=0;
while (count<6)
{
cout << TelephoneNames[count] << endl;
cout << TelephoneNumbers[count] << endl;
cout << endl;
count++;
}
}
void SearchRecords()
{
string name;
int count=0;
cout << "Enter the name of the person you would like to find: ";
cin >> name;
for (count=0;TelephoneNames[count]!=name;count++)//To determine where in the strings the new numbers need to be
{
}
cout << "Name: " << TelephoneNames[count] << endl;
cout << "Number: " << TelephoneNumbers[count] << endl;
}
Since there is no any answer still and I don't see exactly the problem at this point I'll provide some suggestions how you can find a problem in your code.
In any programming situation when you can't find a bug, first task is to locate it as much precisely as you can and check all input data and assumptions. Usually, debugger is used for such purposes, but you can just output text in console before creating final version of your program.
To start with, you must check that you really received names and telephones from your file:
inputFile.open("Telephone Names.txt"); //To store
for (int count=0;count<100;count++) //file names
{ //into a
inputFile >> TelephoneNames[count]; //string
cout << TelephoneNames[count] << endl; //WE MUST SEE WHAT IS REALLY STORED IN TelephoneNames
}
inputFile.close();
inputFile.open("Telephone Numbers.txt");//To store
for (int count=0;count<100;count++) //file #'s
{ //into a
inputFile >> TelephoneNumbers[count];//string
cout << TelephoneNumbers[count] << endl; //WE MUST SEE WHAT IS REALLY STORED IN TelephoneNumbers
}
inputFile.close();
Ok, when it is checked and you are defenitely sure there is no problem in your data we can move to SeaerchRecords function doing the same procedure. We must check what is happening while you are searching:
for (count=0;TelephoneNames[count]!=name;count++)//To determine where in the strings the new numbers need to be
{
cout << "Search step: " << count << " name " << name << " found name " << TelephoneNames[count] << " number " << TelephoneNumbers[count] << endl;
}
Doing so you will locate your bug rather quickly. The problem can be in input files format, in difference of "name" and stored names format etc.
I'll provide several additional suggestion how you can improve your code.
1) Try to use const declarations for such commonly used things as number of records (const int NUMBER_OF_RECORDS = 100; insted of just putting '100' everywhere), it will reduce the amout of work and possible bugs. 2) Try to check all possible problems that you program can encounter if someting is wrong with data. What will happen if you have less than 100 records in your files now? Program crush or silent reading of unappropriate data which is even worse. Check that you haven't reach file end on any step of reading along with current check that you've reached you number of records and do something in case of unappropriate data.
3) Check the possible problems with conditions in your cycles not to run them infinite number of times. Now your condition for(count=0;TelephoneNames[count]!=name;count++)
will execute forever if there is no such name or just crush the program on count 100 or more. You should check that count doesn't exceed that value. Good luck!
I'm trying to solve this issue where when i try and search for a certain module name in my .dat file, it doesn't show the info of some module like module name, module code.
Example: if I search CSCI124, it shows all of the needed info i need in the output.
However if i try searching for CSCI114 or MATH121, it doesn't show any info except for "Subject Code not found.."
I have tried playing around with not putting in the while loop however it doesn't work as well.
It would be awesome if you guys could help me out, just started learning about c++
Subject subjectDB;
char subCode[MAX];
int printOnce = 0;
int position = 0;
cout << "Enter Subject Code: ";
cin >> subCode;
// Open binary file
ifstream fin("Subject.dat", ios::out | ios::binary);
if (!fin)
{
cout << "\nError opening database..\n"
<< "\tQuitting System..";
exit(-1);
}
cin.clear();
cin.ignore(100, '\n');
while(fin.read(reinterpret_cast<char*>(&subjectDB), sizeof(Subject)))
{
if (!(strcmp(subCode, subjectDB.subjectCode) == 0))
{
// Print this section once
if (printOnce == 0)
{
cout << "Subject Code not found..\n";
printOnce++;
}
}
else
{
// Print this section once
if (printOnce == 0)
{
cout << "\nSubject Code: "
<< subjectDB.subjectCode
<< "\nSubject Name: "
<< subjectDB.subjectName
<< "\n"
<< endl;
cout << "Task\t"
<< "Title\t\t"
<< "Weight\t"
<< "Upon\t"
<< "Mark\t"
<< "Obtained\n";
// PrintOnce++ : 1 != 0
// So it only prints once
printOnce++;
}
cout << position + 1
<< "\t"
<< subjectDB.assessment[position].title
<< "\t"
<< subjectDB.assessment[position].weight
<< "\t"
<< subjectDB.assessment[position].upon
<< "\t"
<< subjectDB.assessment[position].taskMark
<< "\t"
<< "testing\n";
position++;
}
}
Writing a struct to a file or reading a struct from a file is totally non-portable. Compile the same code on two different compilers, or on the same compiler with different settings, and what you wrote and what you read might be very different. That's not necessarily your problem, but it will be your problem one day.
Your display code logic is flawed.
Explanation:
If the subject code is in the first record you read, you'll get the correct answer. However as soon as your fist record does not match, the "Not found" message is displayed, printOnce is incremented. If the matching subject coe is found later in the file printOnce is no longer 0 and it' will not be displayed.
Solution:
Organise your loop in the opposite way:
while (...) {
if (strcmp(..)==0) {
// your code to display the found item here
printOnce++;
break; // ?? optional: you could stop the loop at first found occurence unless you suppose there could be duplicates
}
}
and once the loop is finished, check OUTSIDE OF THE LOOP if you've found something:
if (printOnce==0) { // nothing was found in the loop
// display that nothing was found !
}
Remark:
Reading directly from file as you do has limitations. It can only work with plain old data (POD), not with more advanced types using string members or containters. For learning it's a good start but I'd suggest to foresee a Subject member function that loads the data from a stream. This proves to be more flexible when your data structure evolves: the member function could ealsily read each member data and sub-objects using the most apporpriate way.
Thanks for your help! I managed to get the function to compare properly against user input and struct file by creating a function that checks the .dat file if the subject is existing or not.
int row = checkNumberOfData(fileName);
if (row > 0)
exist = doesSubjectExist(file, fileName, code); // checks input
if (!exist)
{
file.open(fileName, ios::out | ios::app | ios::binary);
if (!file)
{
cout << "Error opening database..\n"
<< "\tQuitting System ..\n";
exit(-1);
}
strcpy(subjectDB.code, code);
cout << "Subject Name: ";
cin.getline(subjectDB.name, MAX);
cout << "No of assessment tasks: ";
cin >> subjectDB.num;
cout << endl;
.... etc
}
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.
I am in the second phase of a project where I need to extend my program into a menu driven application to query the database I have on a .txt file. So, my trouble is that I cannot get my loop to be perpetual. It always terminates when it initializes from one option to the next. Here is the snippet of my code that is my int main:
int main ()
{
char Q,q;
char S,s;
char task;
string pathname;
string z;
int count=0;
cout << "Welcome to Jason Rodriguez's Library Database." << endl;
cout << "Please enter the name of the backup file: ";
cin >> pathname;
ifstream inFile(pathname.c_str());
while(!inFile.eof())
{
getline(inFile,z);
count++;
}
while (task != 'Q' || task != 'q') {
cout << count << " records loaded successfully." << endl;
cout << "Enter Q to (Q)uit, Search (A)uthor, Search (T)itle, (S)how All: ";
cin >> task;
if ((task == 'Q')||(task =='q'))
{
cout << "Program will now terminate";
break;
}
else if ((task == 'S')||(task =='s'))
{
showAll (loadData (pathname));
cout << endl;
cout << "Enter Q to (Q)uit, Search (A)uthor, Search (T)itle, (S)how All: ";
cin >> task;
}
}
}
I need to add two more options into the loop on top of these two but I figured I should get the first two working correctly first. The other two should be plug & chug after that. Basically what I was trying to do is say if the user enters Q or q, terminate the program. Else, if user hits S or s, activate showall function and after ward, go back to the original query. It isn't working though. Assistance is welcome and appreciated.
Menus almost always require loops - especially ones that require the user to enter the correct choice input. The most applicable one in a case like this is the while loop - but essentially, any other loop variant can be used.
UPDATE:
int main ()
{
char task;//this is the only char needed. Your other chars were redundant
string pathname;
string temp;//I changed z to temp to better reflect its purpose
int count=0;
cout << "Welcome to Jason Rodriguez's Library Database." << endl;
cout << "Please enter the name of the backup file: ";
cin >> pathname;
ifstream inFile(pathname.c_str());//this is potentially a problem in that you aren't verifying that the pathname is a valid one
//you did not check to see that your file was open, otherwise there is no way to tell that you successfully opened the file
if (inFile.is_open()) {
//while(!inFile.eof()) is a character by character read and comparison
//made your life easier by shortening it down to this - which ensures
//that a line is read. (Much faster and more readable)
while(getline(inFile,temp))
{
count++;
}
inFile.close();//always close a file after you've used it
//At this point the entire file has been read. So, this is where this message SHOULD be
cout << count << " records loaded successfully." << endl;
}
else {
//if there was an error opening the file (i.e. wrong path, or it simply does not exist), this will be displayed
cout << "There was a problem opening your file" << endl;
exit(0);//and the program will terminate
}
while (task != 'Q' || task != 'q') {
cout << "Enter Q to (Q)uit, Search (A)uthor, Search (T)itle, (S)how All: ";
cin >> task;
if ((task == 'Q')||(task =='q'))
{
cout << "Program will now terminate";
break;
}
else if ((task == 'S')||(task =='s'))
{
string author;
//showAll (loadData (pathname));
cout << endl;
cout << "Search an Author" << endl;
cin >> author;//get the author name to search from the user
//write code to search an author here
}
}
}
There are a number of issues with the code that you posted which I will forgo for the sake of brevity. Hence, note the following:
Your code was printing the same message per option (except for quit). Of course it would appear that it didn't work. Each option is a different task. Print what each task does (similar to what I did).
You wish to search the file for an author, but you have not stored it. Look into a way of storing it that appeases your instructor.
It would be ideal for you to use switch in this case, considering the increasing complexity of your code.
Try breaking down each task into functions, and call them to make your main function readable. In fact, it is a good programming practice for your main function to be as small as possible.
And, as juanchopanza quite rightly pointed out: you have some fundamental issues with C++. Try doing some more exercises and do more examples from a good C++ book.