Quote:while(!file.eof()) { // stuff }if(file.eof()) exit(2);
In plain English: "Until you reach the end of the file, read stuff from the file and do stuff with it. Then, after that, if you have reached the end of the file, panic!"
But, you know, observing the end of the file when you have just finished "doing stuff until the end of the file" is exactly what you should expect to happen. :)
(Oh, I see, you wanted to break out when you found the right line, and then parse that line. Sorry: after reading the line, the file is positioned *after* that line. And I expect there's normally a very good chance that the line you want is the last line in the file ;) )
Anyway, you have big, BIG problems here. First of all, use std::string to represent textual data. :) Second, this reading and writing to the same file is HUGELY sketchy. Normally, when you write to an output file, you just overwrite the whole thing. "If the character's data is in there", you'll want to replace it with the updated *anyway*. What are you hoping to gain by appending instead, and how in the hell are you expecting the code as written to make a decision about that?
Oh, wait. Did you mean that the file represents several characters, and you want to overwrite the line with the character's previous data if you update for a character with the same name? Sorry, file I/O doesn't work like that. The new data that you write in won't necessarily be the same length, and the other stuff won't be automatically moved around to fit.
The normal way of handling files like this is:
- Open the file for reading. Read EVERYTHING in. Into an intelligently designed data structure.
- Change the parts that need changing.
- CLOSE AND REOPEN the file for writing. Write EVERYTHING out, overwriting the entire old contents of the file.
For the reading, you have huge problems as well. First of all, the answer to your implicit question about what to do instead of exit() is: throw an exception. Then, reading in the values simply doesn't work like what is shown: you have to account for the ':' characters.
I'm going to hold your hand for this, because there's no way I can sanely expect you to figure it all out on your own.
// You need to make sure you have 'string' and 'sstream' in your includes now.// Please note the change to the return type and values.bool Player::SaveToFile(const string& filename) { // Don't declare a "blank" fstream and then open it later. Instead do this: ifstream file(filename.c_str()); // Note that by specifying an *i*fstream, I implicitly requested just read // access this time around. Trying to read and write a file at the same time // is usually asking for pain. int i = 0; // Here's a buffer where I will store lines from the file that I don't // want to change. It also provides a stream interface, so I can "write out" // to it temporarily while reading in from the real file, and then write the // whole thing to file when I'm done. sstream modifiedFile; // Here's a buffer for the current line, since we're processing the file // a line at a time. string line; // And also a buffer for the current name. string nameFromFile; if (!file.is_open()) return false; while(!file.eof()) { // To read into a std::string, we use the free function version of // getline(), rather than the stream member function. getline(file, nameFromFile, ':'); getline(file, line); // the rest of the input line. // Now here's some magic: with std::string, this works! if (name == nameFromFile) { // Then we should append our modified record to the output. modifiedFile << name << ':' << HP << ' ' << MP << ' ' << level << ' ' << exp << ' ' << hit << ' ' << str << ' ' << def << ' ' << intell << ' ' << focus << '\n'; // Note that there is no reason you can't put '\n' in single quotes the // same way that you put ':' in single quotes. It is 2 bytes of source // code, but it *represents* one byte of data. // Note that I'm putting spaces instead of every colon except the last. // That makes it easier to parse in the reading routine, because the >> // operator skips whitespace, but not punctuation between numbers. // The ':' is only useful to separate the text name from the numeric // items in the rest of the line. } else { // Otherwise, we should append the line from the file. That consists of // the name we just read (up to the ':'), followed by the rest of the line // (up to the '\n'). modifiedFile << nameFromFile << ':' << line << '\n'; } } // Now I can close the file and reopen it, with an ofstream. file.close(); ofstream ofile(filename.c_str()); // And dump all of the buffered file contents at once: ofile << modifiedFile.str() << flush; // "flush" is like "endl", but without actually writing a newline: it // only flushes the buffer and makes sure everything is written. // (You don't need to flush stringstream writes, but you do with files and // the console.) // I don't need to write 'ofile.close()' here, because file closing is // automatically done by the destructor of fstream objects. The reason I // explicitly closed the 'ifstream file' is because that has to happen // *before* we reopen the same file, and the destructor doesn't happen until // end of scope.}Player::Player(const string& filename, const string& name) : name(name) { // I use the initializer list to initialize things that are known right away. // In our case, that's the Player name: as long as the object will be // constructed OK, the 'name' will be set to the provided name. There's no // need to read the file to determine that much - if it doesn't match in // the file then we're going to exception out anyway :) ifstream file(filename.c_str()); if (!file.is_open()) throw std::exception(); // this can be refined later // Borrowing a technique from before... string line; string nameFromFile; // This is one example of a common idiom with file reading: // It says "while possible to do this read... (oh, and please actually do the // read if you can)" while (getline(file, nameFromFile, ':')) { if (nameFromFile == name) { // OK, use the rest of this line to initialize the object. file >> HP >> MP >> level >> exp >> hit >> str >> def >> intell >> focus; curHP = HP; curMP = MP; // Nothing stops you from chaining the reads together the same way you // do with writes. return; // We finished. // No need to close the file here, as noted before. } else { // Skip the rest of this line. // Actually, there are better ways to do that, but I'm leaving it like // this for symmetry and to avoid overloading you ;) getline(file, line); } } // If we get here, the line wasn't found. throw std::exception(); // No need to close the file here, as noted before.}