C++ Save a text document - c++

I've been working on my game and I can easily save my text document, everything works perfectly. My question is, how can I save the text document into a file called "Saves." Here is my code.
Also! I get the input for the char* name from
Save(gets(new char [50]));
Why won't this code work right here?
char* newArray = new char[strlen("PaintAPicture/")+strlen("Saves/")+strlen(name)+strlen(".asciip")+1];
strcpy(newArray,"PaintAPicture/");
strcpy(newArray,"Saves/");
strcpy(newArray,name);
strcat(newArray,".asciip");
I took what you said about using a string, but it's not creating the file and I get the Saving Failed, Main Problem error.
if(saveFile)
{
system("cls");
string prename;
cout << "Enter level's number: ";
cin >> prename;
string name = "Files/" + "Saves/" + prename + ".asciip";
ofstream out(name, ios::binary);
if (!out.is_open()){ MessageBox( 0, "Saving failed! Main problem.", 0, MB_ICONERROR); system("cls"); RedrawMap(); return 0; }
system("cls"); cout << "Saving...";
system("cls");
ShowConsoleCursor(false);
cout << "Saving...";
Sleep(1000);
for(int i = 9; i < SizeY; i++)
{
for(int j = -1; j < SizeX; j++)
{
out << Map[i][j].ch << endl;
out << (int)Map[i][j].color << endl;
}
}
}
cout << '\a';
out.close();
}

Prepend the filename with "Saves/"
char* newArray = new char[strlen("Saves/")+strlen(name)+strlen(".asciip")+1];
strcpy(newArray,"Saves/");
strcat(newArray,name);
strcat(newArray,".asciip");
Also std::string class is designed for storing and manipulating strings. Much easier to use and a lot less error prone than C-strings. Info on string.
EDIT:
Your first piece of code
char* newArray = new char[strlen("PaintAPicture/")+strlen("Saves/")+strlen(name)+strlen(".asciip")+1];
strcpy(newArray,"PaintAPicture/");
strcpy(newArray,"Saves/");
strcpy(newArray,name);
strcat(newArray,".asciip");
doesn't work becouse you're using strcpy where you should use strcat.
strcpy(newArray,"Saves/");
strcpy(newArray,name);
should be
strcat(newArray,"Saves/");
strcat(newArray,name);
As for the problem with creating the file, do those folders exist already? ofstream can create new files to a specified folder, but it cannot create new folders. See https://stackoverflow.com/a/9089919/4761271

Related

C++: How to display the last five names/lines when reading from a file?

I've been trying to get this function to read the last five lines of a file. This is all I have so far. A similarly structured function that finds the first five lines and displays them worked fine and so does the part of this function that counts up the total number of lines.
Everything else seems to work fine except this part and I'm not sure what I'm doing wrong. I tried multiple different solutions (aka just moving the code around and deleting lines to see if it would help) but nothing worked. It can, however, display 46-50 with the colons but not the last five lines. If I were to display the names before the for loop part and within the while loop counting part, it would successfully display all of the names.
Help would be appreciated.
Thanks. (sorry if I'm not formatting this correctly)
void displayLastFive(ifstream & fin)
{
char name[81];
int totalNumberOfLines = 0;
fin.getline(name, 81);
fin.clear();
fin.seekg(0L, ios::beg);
while (!fin.eof())
{
fin.getline(name, 81);
totalNumberOfLines++;
}
for (int i = totalNumberOfLines - 5; i < totalNumberOfLines; i++)
{
fin.getline(name, 81);
cout << i + 1 << ": " << name << endl;
}
cout << endl;
}
answer was much simpler than I thought
void displayLastFive(ifstream & fin)
{
char name[81];
int totalNumberOfLines;
totalNumberOfLines = count(fin);
fin.clear();
fin.seekg(0L, ios::beg);
for (int i = 0; i < totalNumberOfLines; i++)
{
fin.getline(name, 81);
if (i >= totalNumberOfLines - 5)
{
cout << i + 1 << ": " << name << endl;
}
}
cout << endl;
}

Program crash on file output c++

My program crashes unexpectedly when I try and output my results to a .txt file.
Basically, I want to store a list of students in a .txt file and retrieve them for later convenience. My program can store the values as variables, however the moment you want to save them to the output file, the program itself crashes.
This is my code
//STUDENTS CLASS
char** studentNames;
int numberOfNames;
void Students::setNumberOfNames(int namenumbers) // Takes in the number of student names to store
{
numberOfNames = namenumbers;
}
void Students::setStudentNames() // Takes a number of student names and stores them.
{
char studentinput[128]
studentNames = new char*[numberOfNames];
for (int i=0; i<=numberOfNames; i++)
{
studentNames[i] = new char[128];
cout << "Student " << i << ": " << "\n";
cin.getline(studentinput, sizeof(studentinput));
strcpy(studentNames[i], studentinput);
}
}
//MAIN CLASS
Student s;
int nums;
int main()
{
cout << "How many names would you like to store? " << endl;
cin >> nums;
s.setNumberOfNames(nums):
s.setStudentNames();
for(int i=0; i<=s.numberOfNames; i++)
{
cout << s.numberOfNames [i] << "\n"; // THIS WORKS FINE! DOES WHAT I SAY :)
}
// THIS IS WHERE IT CRASHES. I TRY AND STORE THE NAMES INTO THE `.txt` FILE.
//IF YOU WANT TO STORE 3 NAMES, IT WORKS FINE, BUT IF YOU WANT TO STORE LIKE
//12 NAMES THE WHOLE THING CRASHES. WHEN I COMMENT OUT THE BOTTOM BLOCK
//OF CODE THE PROGRAM WORKS FINE, BUT I WANT TO STORE THE NAMES.
ofstream outputFiles ("example.txt");
if(outputFiles.is_open())
{
for(int i=0; i<=s.numberOfNames; i++)
{
outputFiles << s.studentNames [i] << "\n";
}
}
}
Change
for (int i=0; i<=numberOfNames; i++)
to
for (int i=0; i < numberOfNames; i++)
everywhere in your program.

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 << '#';
}

c++ for loop with decrement update causing infinite loop?

This is part of a greater code for reading an input file word-for-word, then printing the words in reverse order. It uses a string array called words[] to store, word-by-word, the char strings from an input file earlier in the program:
//print to screen
for (int i = MAXSIZE; i >= 0; i--)
{
cout << words[i] << " ";
}
Test input file contents:
This is my test file. I hope this works.
Output is just "works. " repeating on and on.
Why is the i-- apparently never happening?
EDIT: Everything from my code. I'm on a bit of a time crunch here, to say the least. MAXSIZE=1024 part of lab prompt. Can't use vectors or reverse; seen that all over, but it's off limits for this lab. New to programming, so if you could refrain from being condescending, that'd be great. Just trying to get this to work. The reading input.txt and print to screen bit works fine. Output portion is utter fail and I don't know why. Can someone just tell me why instead of insulting me, thanks?
//Kristen Korz
//CIS 22A
//This program reads an input file and writes the words in reverse order to an output file.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
//create and link input...
ifstream inputFile;
inputFile.open("input.txt");
//...and output files
ofstream outputFile;
outputFile.open("output.txt");
//error message for file open fail
if (inputFile.fail())
cout << "Error opening the file.\n";
//constant for max size
const int MAXSIZE = 1024;
//string array and temporary-use string
string words[MAXSIZE];
string str; //note: variables will be used for output loops too
//read words from input file
for (int i = 0; (inputFile >> str) && (i < MAXSIZE); ++i)
{
words[i] = str;
//for showing in terminal if read correctly
cout << words[i] << " ";
}
inputFile.close();
cout << endl;
//something wrong with for loop resulting in i apparently not updating
for (int i = MAXSIZE; (outputFile << str) && (i >= 0); --i)
{
words[i] = str;
//for showing in terminal if written correctly
cout << words[i] << " ";
}
outputFile.close();
cout << endl;
system("pause");
return 0;
}
For output with i also printed, my cout statements in the for-loops say:
cout << words[i] << " " << i << " ";
Giving terminal output:
This 0 is 1 my 2 test 3 file. 4 I 5 hope 6 this 7 works. 8
works. 1023 works. 1022 works. 1021 (lots of repeats of works. followed by decrementing numbers) works. 3 works. 2 works. 1 works. 0
Your output loop does:
words[i] = str;
for every iteration. str still holds the value of the last string you input, so this sets every member of words to be the same string. Since your last input string was "works", this explains why you output "works" every time.
It should work better if you just remove that line. Also, start from MAXSIZE - 1. The valid indices of the array are 0 through MAXSIZE-1. Your out-of-bounds access causes undefined behaviour, although apparently in this instance it had no effect.
However if your input only has 8 words as you suggest, then outputting 1024 words will give you a lot of blank space. Consider starting the output from where i got up to, instead of MAXSIZE - 1.
At the part marked as not working (the second for loop), str is being read from but it is never changed to anything else in that loop, so it repeats the last word. i is being updated, the problem is that str is not being updated.
The other issue is that you are trying to access an element past the end of the array, as WhozCraig and Velthune discussed in their answers. You need to properly figure out what you want to do with words in your second for loop. This is key. Also, you need to store where the array you read in ends.
Viewing WhozCraig's link, if you have:
const int MAXSIZE = 1024;
string words[MAXSIZE];
for (int i = MAXSIZE; i >= 0; i--) {
cout << words[i] << " ";
}
You have a string that from 0..1023.
Accessing words[1024] is potentially dangerous.
For iterate correctly your string do:
for (int i = MAXSIZE - 1; i >= 0; --i) {
cout << words[i] << " ";
}
By the way, when you fill words, add a control:
for (int i = 0; (inputFile >> str) && (i < MAXSIZE); ++i)) {
if(str.size() <= MAXSIZE) {
words[i] = str;
}
}
update
Be sure that your string in file:
"This is my test file. I hope this works. "
doesn't end with a space. To be sure, test adding "EOF" to your string:
"This is my test file. I hope this works.EOF"
Other, do your loop in this way:
int i = 0;
while(inputFile.good() && i < MAXSIZE) {
std::string word << inputFile;
if(!word.empty())
words[i] = str;
//for showing in terminal if read correctly
cout << words[i] << " ";
}
The problem why you get a lot of "works" here is:
After this piece of codes:
//read words from input file
for (int i = 0; (inputFile >> str) && (i < MAXSIZE); ++i)
{
words[i] = str;
//for showing in terminal if read correctly
cout << words[i] << " ";
}
inputFile.close();
cout << endl;
//str = "works";
The values of variable str is works.
After that, you set every elements in words by str. So every elements in the words now are the same value works.
for (int i = MAXSIZE; (outputFile << str) && (i >= 0); --i)
{
words[i] = str;//=="works"
//for showing in terminal if written correctly
cout << words[i] << " ";
}
outputFile.close();
cout << endl;

std::getline returning the wrong size

something wrong with getline(), taking the words in correct but still the value of size remains 26.
I tried printing each time it takes in a character and all of them do print so itis taking in strings correctly, but not storing them?
I have attached the code below to refer
Ask me for the whole project if you need to refer what is going wrong if someplace else.
void TldPart::PreloadTLDs()
{
ifstream in(TLD_TEST_FILE);
if(in)
{
string tld;
for(int i =0; !in.eof(); i++)
{
getline(in,tld);
String myString = tld.c_str();
//cout << myString.GetLength() << endl;
for(int j=0; j<myString.GetLength();j++)
{
myString[j]=tolower(myString[j]);
}
//cout << myString << endl;
ValidTLDs.insert(pair<String,int>(myString,i));
//ValidTLDs[myString] = true; //if the map was bool
}
in.close();
cout << ValidTLDs.size(); //Printing the size //prints 26
}
}