I noticed in my C++ code that anytime I close an std::ofstream
object I'm unable to reopen the file I closed with std::ifstream
. std::ifstream
's open
function will always fail.
Is there anything 'extra' I can do to ensure that my std::ofstream object closes properly?
Someone's probably going to ask to see my specific code so for the sake of keeping this post small I've pastied it here. In my code after running through case a or d all std::ifstream
open calls fail. (Prior to posting this question I had several people play with my code who were unable to conclude anything other than that std::ofstream
close failing for unknown reasons)
Thanks in advance to any and all responses received.
Code is
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
typedef struct Entry
{
string Name;
string Address;
string Phone;
};
int main()
{
bool exit = false, found = false;
char input, ch;
string stringinput, stringoutput;
ifstream fin, ibuffer;
ofstream fout, obuffer;
Entry buffer;
while(!exit)
{
cout << "Welcome to the Address Book Application!" << endl << endl;
cout << "\tSelect an option:" << endl << endl;
cout << "\tA -- Add New Entry" << endl;
cout << "\tD -- Delete an Entry" << endl;
cout << "\tS -- Search for an Existing Entry" << endl;
cout << "\tE -- Exit" << endl << endl;
cin >> input;
switch(input)
{
case 'a':
case 'A':
cin.ignore(255,'\n');//Apparently necessary because an extra new line carrys over in the cin stream
system("cls");
//Get Information from User
cout << "Enter Phone Number: ";
getline(cin, buffer.Phone);
cout << endl << "Enter Name: ";
getline(cin, buffer.Name);
cout << endl << "Enter Address: ";
getline(cin, buffer.Address);
/*Copy existing database into a buffer. In other words, back it up*/
fin.open("address.txt");
if(!fin)
{
fin.close();
fout.open("address.txt");
fout << buffer.Phone << endl << buffer.Name << endl << buffer.Address << endl;
}
if(fin)
{
obuffer.open("buffer.txt");
while(fin && fin.get(ch))
obuffer.put(ch);
fin.close();
obuffer.close();
/*Copy buffer to new database file*/
ibuffer.open("buffer.txt");
fout.open("address.txt");//This removes all of the previously existin开发者_Go百科g info from database.txt
while(ibuffer && ibuffer.get(ch))
fout.put(ch);
ibuffer.close();
remove("buffer.txt");//Delete the buffer
fout << endl << buffer.Phone << endl << buffer.Name << endl << buffer.Address << endl;
}
buffer.Phone.erase();
buffer.Name.erase();
buffer.Address.erase();
fout.close();
break;
case 'd':
case 'D':
cin.ignore(255,'\n');//Apparently necessary because an extra new line carrys over in the cin stream
system("cls");
cout << "Enter the phone number of the entry to delete: ";
cin >> stringinput;
fin.open("address.txt");
if(!fin)
{
cout << endl << "No entries exist!";
fin.close();
break;
}
obuffer.open("buffer.txt");
/* Copy everything over into the buffer besides the account we wish to delete */
while(!fin.eof())
{
fin.read(&ch, sizeof(char));
if(ch != '\n' && ch != '\0')
stringoutput += ch;
if(ch == '\n' || ch == '\0')
{
if(stringinput.compare(stringoutput))
{
stringoutput += ch;
obuffer << stringoutput;
stringoutput.erase();
}
if(!stringinput.compare(stringoutput))
{
stringoutput += ch;
getline(fin, stringoutput);
getline(fin, stringoutput);
fin.read(&ch, sizeof(char));//Get rid of the extra '\n'
stringoutput.erase();
}
}
}
//Hack: Copy the last line over since the loop for some reason doesn't
obuffer << stringoutput;
stringoutput.erase();
fin.close();
obuffer.close();
fout.open("address.txt");
ibuffer.open("buffer.txt");
while(ibuffer && ibuffer.get(ch))
fout.put(ch);
ibuffer.close();
fout.close();
remove("buffer.txt");
cout << endl << "Entry " << stringinput << " deleted successfully!" << endl;
stringinput.erase();
stringoutput.erase();
break;
case 's':
case 'S':
cin.ignore(255,'\n');//Apparently necessary because an extra new line carrys over in the cin stream
system("cls");
found = false;
fin.open("address.txt");
if(!fin)
{
cout << "No entries currently exist!" << endl << endl;
fin.close();
break;
}
cout << "Enter the phone number to search for: ";
cin >> stringinput;
while(!fin.eof())
{
fin.read(&ch, sizeof(char));
if(ch != '\n' && ch != '\0')
stringoutput += ch;
if(ch == '\n' || ch == '\0')
{
if(!stringinput.compare(stringoutput))
{
found = true;
break;
}
stringoutput.erase();
}
}
if(found)
{
cout << "Phone Number: " << stringinput << endl;
getline(fin, stringoutput);
cout << "Name: " << stringoutput << endl;
getline(fin, stringoutput);
cout << "Address: " << stringoutput << endl << endl;
}
if(!found)
{
stringoutput.erase();
cout << endl << stringinput << " is not in the address book!" << endl << endl;
}
stringinput.erase();
stringoutput.erase();
fin.close();
break;
case 'e':
case 'E':
exit = true;
break;
default:
system("cls");
cout << input << " is not a valid option." << endl << endl;
break;
}
}
return 0;
}
The best way to ensure your streams are reset at each point in this processing is not to explicitly close them, but to declare them at the point of use and allow them to go out of scope. This is the RAII point that was made in comments. In your case this means moving the declaraion from the top of the function to inside each arm of the switch where they are used. This way you can be sure that each file is cleanly closed when you exit from a particular case
, as the ofstream and ifstream close the file during destruction on scope exit.
Another thing I noticed is that you are testing a strange thing after file open:
fin.open("address.txt");
if(!fin)
This tests for a bad stream but I don't think it's complete - if you are testing for a successful open, you should also test
fin.open("address.txt");
if (!fin.is_open())
All of your file processing loops and open()
calls need to check for fin.fail()
or fout.fail()
as well as fin.eof()
, to rule out file access error as an unhandled condition that is confusing your observed behaviour.
At this point, I suggest you fix these obvious misunderstandings, and come back with more specific questions if you cannot get it working.
精彩评论