ifstream.open() not opening file - c++

I've been having a nightmare this evening trying to get some very simple I/O functionality going. As embarrassing as it is, I've had some great help from people on here!
My current issue is that I'm attempting to use ifstream.open() and it simply is not opening the file. This is confirmed by getline(ifstream,line); returning false on it's first call.
Here is a copy paste of the current code:
std::string FSXController::readLine(int offset, FileLookupFlag flag)
{
// Storage Buffer
string line;
streampos sPos(offset);
try
{
// Init stream
if (!m_ifs.is_open())
m_ifs.open("C:\\Users\\guyth\\Documents\\test.txt", fstream::in);
}
catch (int errorCode)
{
showException(errorCode);
return "";
}
// Set stream to read input line
m_ifs.seekg(sPos);
if (!getline(m_ifs, line))
return "";
// Close stream if no multiple selection required
if (flag == FileLookupFlag::single)
m_ifs.close();
return line;
}
This code is in 'bug fix mode' and so therefore is pretty messy, don't worry too much about that, cleanup will happen when this method is finally working.
I have tried:
Absolute file path
Saving path into string and then calling the .c_str() method.
Running VS 2015 in Administrator mode
Ensuring file has read/wright access
Ensuring no duplicate file extensions
Yes the file definitely has content! :D
I'm kinda out of ideas now and am really not sure why this file is refusing to load.
The condition: if (!getline(m_ifs, line)) Repeatedly returns true... :(
EDIT: I've just tried checking m_ifs.fail() immediately after the open and it returns true, so we know the fail flag was triggered :/
Thanks
Guy

Enable exceptions before opening the stream:
m_ifs.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
Otherwise m_ifs.open won't throw.
And you have to catch std::ifstream::failure:
try {
m_ifs.open("C:\\Users\\guyth\\Documents\\test.txt", fstream::in);
}
catch (std::ifstream::failure e) {
std::cerr << "Exception opening file: " << std::strerror(errno) << "\n";
}
See ios::exceptions for more details.

Related

I can't get the ofstream function to work

Hello and sorry if the answer is clear to those out there. I am still fairly new to programming and ask for some guidance.
This function should write just one of the three string parameters it takes in to the txt file I have already generated. When I run the program the function seems to work fine and the cout statement shows the info is in the string and does get passes successfully. The issue is after running the program I go to check the txt file and find it is still blank.
I am using C++17 on visual studio professional 2015.
void AddNewMagicItem(const std::string & ItemKey,
const std::string & ItemDescription,
const std::string &filename)
{
const char* ItemKeyName = ItemKey.c_str();
const char* ItemDescriptionBody = ItemDescription.c_str();
const char* FileToAddItemTo = filename.c_str();
std::ofstream AddingItem(FileToAddItemTo);
std::ifstream FileCheck(FileToAddItemTo);
AddingItem.open(FileToAddItemTo, std::ios::out | std::ios::app);
if (_access(FileToAddItemTo, 0) == 0)
{
if (FileCheck.is_open())
{
AddingItem << ItemKey;
std::cout << ItemKey << std::endl;
}
}
AddingItem.close(); // not sure these are necessary
FileCheck.close(); //not sure these are necessary
}
This should print out a message onto a .txt file when you pass a string into the ItemKey parameter.
Thank you very much for your help and again please forgive me as I am also new to stackoverflow and might have made some mistakes in formatting this question or not being clear enough.
ADD ON: Thank you everyone who has answered this question and for all your help. I appreciate the help and would like to personally thank you all for your help, comments, and input on this topic. May your code compile every time and may your code reviews always be commented.
As mentioned by previous commenters/answerers, your code can be simplified by letting the destructor of the ofstream object close the file for you, and by refraining from using the c_str() conversion function.
This code seems to do what you wanted, on GCC v8 at least:
#include <string>
#include <fstream>
#include <iostream>
void AddNewMagicItem(const std::string& ItemKey,
const std::string& ItemDescription,
const std::string& fileName)
{
std::ofstream AddingItem{fileName, std::ios::app};
if (AddingItem) { // if file successfully opened
AddingItem << ItemKey;
std::cout << ItemKey << std::endl;
}
else {
std::cerr << "Could not open file " << fileName << std::endl;
}
// implicit close of AddingItem file handle here
}
int main(int argc, char* argv[])
{
std::string outputFileName{"foobar.txt"};
std::string desc{"Description"};
// use implicit conversion of "key*" C strings to std::string objects:
AddNewMagicItem("key1", desc, outputFileName);
AddNewMagicItem("key2", desc, outputFileName);
AddNewMagicItem("key3", desc, outputFileName);
return 0;
}
Main Problem
std::ofstream AddingItem(FileToAddItemTo);
opened the file. Opening it again with
AddingItem.open(FileToAddItemTo, std::ios::out | std::ios::app);
caused the stream to fail.
Solution
Move the open modes into the constructor (std::ofstream AddingItem(FileToAddItemTo, std::ios::app);) and remove the manual open.
Note that only the app open mode is needed. ofstream implies the out mode is already set.
Note: If the user does not have access to the file, the file cannot be opened. There is no need to test for this separately. I find testing for an open file followed by a call to perror or a similar target-specific call to provide details on the cause of the failure to be a useful feature.
Note that there are several different states the stream could be in and is_open is sort of off to the side. You want to check all of them to make sure an IO transaction succeeded. In this case the file is open, so if is_open is all you check, you miss the failbit. A common related bug when reading is only testing for EOF and winding up in a loop of failed reads that will never reach the end of the file (or reading past the end of the file by checking too soon).
AddingItem << ItemKey;
becomes
if (!(AddingItem << ItemKey))
{
//handle failure
}
Sometimes you will need better granularity to determine exactly what happened in order to properly handle the error. Check the state bits and possibly perror and target-specific
diagnostics as above.
Side Problem
Opening a file for simultaneous read and write with multiple fstreams is not recommended. The different streams will provide different buffered views of the same file resulting in instability.
Attempting to read and write the same file through a single ostream can be done, but it is exceptionally difficult to get right. The standard rule of thumb is read the file into memory and close the file, edit the memory, and the open the file, write the memory, close the file. Keep the in-memory copy of the file if possible so that you do not have to reread the file.
If you need to be certain a file was written correctly, write the file and then read it back, parse it, and verify that the information is correct. While verifying, do not allow the file to be written again. Don't try to multithread this.
Details
Here's a little example to show what went wrong and where.
#include <iostream>
#include <fstream>
int main()
{
std::ofstream AddingItem("test");
if (AddingItem.is_open()) // test file is open
{
std::cout << "open";
}
if (AddingItem) // test stream is writable
{
std::cout << " and writable\n";
}
else
{
std::cout << " and NOT writable\n";
}
AddingItem.open("test", std::ios::app);
if (AddingItem.is_open())
{
std::cout << "open";
}
if (AddingItem)
{
std::cout << " and writable\n";
}
else
{
std::cout << " and NOT writable\n";
}
}
Assuming the working directory is valid and the user has permissions to write to test, we will see that the program output is
open and writable
open and NOT writable
This shows that
std::ofstream AddingItem("test");
opened the file and that
AddingItem.open("test", std::ios::app);
left the file open, but put the stream in a non-writable error state to force you to deal with the potential logic error of trying to have two files open in the same stream at the same time. Basically it's saying, "I'm sorry Dave, I'm afraid I can't do that." without Undefined Behaviour or the full Hal 9000 bloodbath.
Unfortunately to get this message, you have to look at the correct error bits. In this case I looked at all of them with if (AddingItem).
As a complement of the already given question comments:
If you want to write data into a file, I do not understand why you have used a std::ifstream. Only std::ofstream is needed.
You can write data into a file this way:
const std::string file_path("../tmp_test/file_test.txt"); // path to the file
std::string content_to_write("Something\n"); // content to be written in the file
std::ofstream file_s(file_path, std::ios::app); // construct and open the ostream in appending mode
if(file_s) // if the stream is successfully open
{
file_s << content_to_write; // write data
file_s.close(); // close the file (or you can also let the file_s destructor do it for you at the end of the block)
}
else
std::cout << "Fail to open: " << file_path << std::endl; // write an error message
As you said being quite new to programming, I have explicitly commented each line to make it more understandable.
I hope it helps.
EDIT:
For more explanation, you tried to open the file 3 times (twice in writing mode and once in reading mode). This is the cause of your problems. You only need to open the file once in writing mode.
Morever, checking that the input stream is open will not tell you if the output stream is open too. Keep in mind that you open a file stream. If you want to check if it is properly open, you have to check it over the related object, not over another one.

Why is stream::ignore not working as intended?

As far as I know, stream.ignore(n, 'n') should ignore an (n) amount of characters or if ā€˜\nā€™ is reached, and skip over to the next line, however, when I run the next code:
// include...
void insertInfo(int info) {
std::fstream stream("infoFile.txt"); // Open the file
while (!stream.eof()) {
std::string a{};
// getline(stream, a); <--- Tried this, didn't work either
stream.ignore(99, '\n');
} // Skip to the last line without any number, in theory
std::cout << info << std::endl; // Check if the output it's correct (Which is)
stream << info; // Insert the info
stream.close(); // Close the file
}
void main() //Main
{
std::cout << "Enter your name, followed by the info you want to add to infoFile:" << std::endl;
std::string info, temp = "";
std::getline(std::cin, temp); // Get the info input
std::stringstream sstream;
sstream << temp;
sstream >> temp >> info; // Remove the name keeping only the info
temp = ""; // ^
std::string::size_type sz;
insertInfo(stoi(info, &sz)); // Convert info string into an integer and insert it in infoFile
}
The console prints out the "info" correct value, however, when I check info.txt, in which I previously wrote a '0' on, you don't see any change.
I tried removing the "ignore" function and it overwrites the 0, which is exactly what I was trying to prevent.
I also tried using "getline" function but the same thing happens.
What is the error here?
Problem
Cannot write to file.
Why
void insertInfo(int info) {
std::fstream stream("infoFile.txt"); // Open the file
Opens file with default permissions, which includes reading. The C++ Standard says I should expect "r+" behaviour and the C Standard says a file opened with "r+" behaviour must exist in order to be read (Someone please add a link if you have one). You cannot create a new file. This is problem 1. The Asker has dealt with this problem by providing a file.
Note: take care when working with files via relative paths. The program's working directory may not be where you think it is. This is problem 1a. It appears that the Asker has this taken care of for the moment.
while (!stream.eof()) {
Common bug. For more details see Why is iostream::eof inside a loop condition considered wrong? In this case since all you're looking for is the end of the file, the fact that the file hasn't been opened at all or has encountered any read errors is missed. Since a file in an error state can never reach the end of the file this quickly becomes an infinite loop. This is problem 2.
std::string a{};
// getline(stream, a); <--- Tryied this, didn't work neither
stream.ignore(99, '\n');
Always test IO transactions for success. This call can fail unchecked.
} // Skip to the last line without any number, in theory
Assuming nothing has gone wrong, and since we're not checking the error state assuming's all we can do, the file has reached the end and is now in the EOF error state. We can't read from or write to the stream until we clear this error. This is problem number 3 and likely the problem the Asker is struggling with.
std::cout << info << std::endl; // Check if the output it's correct (Wich is)
stream << info; // Insert the info
This can fail unchecked.
stream.close(); // Close the file
This is not necessary. The file will be closed when it goes out of scope.
}
Solution
void insertInfo(int info) {
std::fstream stream("infoFile.txt"); // Open the file
while (!stream.eof()) {
stream.ignore(99, '\n');
} // Skip to the last line without any number, in theory
std::cout << info << std::endl; // Check if the output it's correct (Wich is)
stream.clear(); // Added a call to clear the error flags.
stream << info; // Insert the info
stream.close(); // Close the file
}
Now we can write to the file. But let's improve this shall we?
void insertInfo(int info) {
std::fstream stream("infoFile.txt");
while (stream.ignore(99, '\n')) // moved ignore here. now we ignore, then test the result
{
}
stream.clear();
stream << info << '\n'; // added a line ending. Without some delimiter the file
// turns into one big number
}
Note that this isn't exactly kosher. If any ignore fails for any reason, we bail out and possibly write over data because the code blindly clears and writes. I'm not spending much time here trying to patch this up because we can get really, really simple and solve the problem of creating a non-existent file at the same time.
void insertInfo(int info) {
std::fstream stream("infoFile.txt", std::ios::app);
stream << info << '\n';
}
Two lines and pretty much done. With app we append to the file. We do not need to find the end of the file, the stream automatically points at it. If the file does not exist, it is created.
Next improvement: Let people know if the write failed.
bool insertInfo(int info) {
std::fstream stream("infoFile.txt", std::ios::app);
return static_cast<bool>(stream << info << '\n');
}
If the file was not written for any reason, the function returns false and the caller can figure out what to do. The only thing left is to tighten up the stream. Since all we do is write to ti we don't need the permissiveness of a fstream. Always start with the most restrictive and move to the least. This helps prevent some potential errors by making them impossible.
bool insertInfo(int info) {
std::ofstream stream("infoFile.txt", std::ios::app);
return static_cast<bool>(stream << info << '\n');
}
Now we use an ofstream and eliminate all the extra overhead and risk brought in by the ability to read the stream when we don't read the stream.

C++ Write file to C drive

My program lets the user specify where to write a file. If they specify "C:\output.txt" the file operations seem to succeed (no errors) however the file doesn't get created. I know this is due to Windows requiring elevated permission and when running as administrator it does write to C:\. My question is how can I detect that the file didn't actually open?
This block "succeeds" without error even though the file doesn't actually get created:
ofstream ofs;
try {
ofs.open(outputFile);
if (!ofs.is_open()){
throw "The file could not be opened";
}
ofs << "it worked";
ofs.close();
} catch (const char* ex){
cout << ex;
return 1;
}
If open() fails to create/open a file, the stream's is_open() method will return false, and the stream's failbit state flag will be set, so the fail() method and operator! will return true:
ofs.open(outputFile);
if (!ofs.is_open())
// or: if (ofs.fail())
// or: if (!ofs)
{
cout << "The file could not be opened";
return 1;
}
If no failure is reported, then no failure occurred. The file was created somewhere, but that might not be where you are expecting.
If you open a file using a relative path, then it is relative to the calling process's current working directory, which may be different than you are expecting. So always use absolute paths.
If you try to create a file and you don't have access to the folder where you are creating the file, the file creation might get transparently redirected to a VirtualStore folder within the user's profile instead.
Try using SysInternals Process Monitor to see where exactly the file is being created (or even, where open() is attempting to create the file, if no access is allowed and redirection doesn't happen).
On a side note, what you showed is a misuse of exception handling. You don't really need an exception at all, as shown above. However, if you do want to throw an exception on failure, consider using the ofstream::exceptions() method instead:
ofstream ofs;
ofs.exceptions(ofstream::failbit);
try {
ofs.open(outputFile);
ofs << "it worked";
}
catch (std::ios_base::failure &) {
cout << "The file could not be opened, or written to";
return 1;
}
ofs.close();

C++ File not read?

I want to read a file line by line. Did something like
void Parse (string filepath) {
ifstream sourceFile;
sourceFile.open(filepath);
for (string line; getline(sourceFile, line);) {
cout << "1" << endl;
cout << line << endl;
}
}
int main() {
Parse("C:\\test.txt");
getchar();
return 0;
}
Then put some text into C:\test.txt, but when I run, I dont get anything. Why? Not even the "1". I notice no exception if the file is not there too. I suppose that a sign of a problem?
You have to check for success/error manually. Try with ifstream::good():
sourceFile.open(filepath);
if(!sourceFile.good()) {
// do something
If you don't want to check manually, you can enable exceptions:
// call that before open()
sourceFile.exceptions ( ifstream::failbit | ifstream::badbit );
I think you have problems opening the file. I would suggest two things:
check if sourceFile is opened successfully(if (sourceFile))
debug the code and see the code path your code follows.
EDIT: adding the actual solution to the problem in my answer(instead of just a comment) so that people won't miss it:
Here is one more thought - check the file name in its properties. Has happened to me that if windows hides the extension of the file the name is actually test.txt.txt, while what I see displayed is only test.txt.
change your for loop to
for (string line; sourceFile.good();) {
getline(sourceFile, line);
}
This way, you check the validity of your stream in the conditional part of the for, and get the line if the stream good.

What's the correct way to do a 'catch all' error check on an fstream output operation?

What's the correct way to check for a general error when sending data to an fstream?
UPDATE: My main concern regards some things I've been hearing about a delay between output and any data being physically written to the hard disk. My assumption was that the command "save_file_obj << save_str" would only send data to some kind of buffer and that the following check "if (save_file_obj.bad())" would not be any use in determining if there was an OS or hardware problem. I just wanted to know what was the definitive "catch all" way to send a string to a file and check to make certain that it was written to the disk, before carrying out any following actions such as closing the program.
I have the following code...
int Saver::output()
{
save_file_handle.open(file_name.c_str());
if (save_file_handle.is_open())
{
save_file_handle << save_str.c_str();
if (save_file_handle.bad())
{
x_message("Error - failed to save file");
return 0;
}
save_file_handle.close();
if (save_file_handle.bad())
{
x_message("Error - failed to save file");
return 0;
}
return 1;
}
else
{
x_message("Error - couldn't open save file");
return 0;
}
}
A few points. Firstly:
save_file_handle
is a poor name for an instance of a C++ fstream. fstreams are not file handles and all this can do is confuse the reader.
Secondly, as Michael pints out, there is no need to convert a C++ string to a C-string. The only time you should really find yourself doing this is when interfacing with C-style APIS, and when using a few poorly designed C++ APIs, such as (unfortunately) fstream::open().
Thirdly, the canonical way to test if a stream operation worked is to test the operation itself. Streams have a conversion to void * which means you can write stuff like this:
if ( save_file_handle << save_str ) {
// operation worked
}
else {
// failed for some reason
}
Your code should always testv stream operations, whether for input or output.
Everything except for the check after the close seems reasonable. That said, I would restructure things slightly differently and throw an exception or use a bool, but that is simply a matter of preference:
bool Saver::output()
{
std::fstream out(_filename.c_str(),std::ios::out);
if ( ! out.is_open() ){
LOG4CXX_ERROR(_logger,"Could not open \""<<filename<<"\"");
return false;
}
out << _savestr << std::endl;
if ( out.bad() ){
LOG4CXX_ERROR(_logger,"Could not save to \""<<filename<<"\"");
out.close();
return false;
}
out.close();
return true;
}
I should also point out that you don't need to use save_str.c_str(), since C++ iostreams (including fstream, ofstream, etc.) are all capable of outputting std::string objects. Also, if you construct the file stream object in the scope of the function, it will automatically be closed when it goes out of scope.
Are you absolutely sure that save_file_handle doesn't already have a file associated (open) with it? If it does then calling its open() method will fail and raise its ios::failbit error flag -- and any exceptions if set to do so.
The close() method can't fail unless the file isn't open, in which case the method will raise the ios::failbit error flag. At any rate, the destructor should close the file, and do so automatically if the save_file_handle is a stack variable as in your code.
int Saver::output()
{
save_file_handle.open(file_name.c_str());
if (save_file_handle.fail())
{
x_message("Error - file failed to previously close");
return 0;
}
save_file_handle << save_str.c_str();
if (save_file_handle.bad())
{
x_message("Error - failed to save file");
return 0;
}
return 1;
}
Alternatively, you can separate the error checking from the file-saving logic if you use ios::exceptions().
int Saver::output()
{
ios_base::iostate old = save_file_handle.exceptions();
save_file_handle.exceptions(ios::failbit | ios::badbit);
try
{
save_file_handle.open(file_name.c_str());
save_file_handle << save_str.c_str();
}
catch (ofstream::failure e)
{
x_message("Error - couldn't save file");
save_file_handle.exceptions(old);
return 0;
}
save_file_handle.exceptions(old);
return 1;
}
You might prefer to move the call to save_file_handle.exceptions(ios::failbit | ios::badbit) to the constructor(s). Then you can get rid of the statements that reset the exceptions flag.