Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I can't get the ofstream function to work

Tags:

c++

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.

like image 233
RookieProgrammer Avatar asked Nov 20 '25 23:11

RookieProgrammer


2 Answers

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;
}
like image 142
jpmarinier Avatar answered Nov 22 '25 11:11

jpmarinier


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).

like image 42
user4581301 Avatar answered Nov 22 '25 12:11

user4581301