6

I'm trying to understand the answer provided here, but I can't seem to make it work.

Here is what I've tried:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <fstream>

int main()
{
    std::string path("numbersfile");

    std::vector<int> myVector{1,16,32,64};
    std::vector<int> newVector{};

    std::ofstream FILE(path,std::ios::out | std::ofstream::binary);
    std::copy(myVector.begin(),myVector.end(),std::ostreambuf_iterator<char>(FILE));

    std::ifstream INFILE(path,std::ios::in | std::ifstream::binary);
    std::istreambuf_iterator<char> iter(INFILE);
    //std::copy(iter.begin(),iter.end(),std::back_inserter(newVector)); //this doesn't compile
    std::copy(iter,std::istreambuf_iterator<char>{},std::back_inserter(newVector)); // this leaves newVector empty
}

newVector is still empty after the last copy. How could the last statement be updated to populate newVector?

Community
  • 1
  • 1
wally
  • 10,717
  • 5
  • 39
  • 72

2 Answers2

13

The file is not ready to be read by the time the second copy is called. (Thanks to Piotr Skotnicki for his answer in the comments)

A call to flush allows the program to work:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <fstream>

int main()
{
    std::string path("numbersfile");

    std::vector<int> myVector{1,16,32,64};
    std::vector<int> newVector{};

    std::ofstream FILE(path,std::ios::out | std::ofstream::binary);
    std::copy(myVector.begin(),myVector.end(),std::ostreambuf_iterator<char>(FILE));
    FILE.flush(); // required here

    std::ifstream INFILE(path,std::ios::in | std::ifstream::binary);
    std::istreambuf_iterator<char> iter(INFILE);
    //std::copy(iter.begin(),iter.end(),std::back_inserter(newVector)); //this doesn't compile
    std::copy(iter,std::istreambuf_iterator<char>{},std::back_inserter(newVector)); // this leaves newVector empty
    return 0;
}

The ofstream is still in scope when the ifstream is created. Had the ofstream's destructor been called then the file would also have been ready for the ifstream. In the following program the ifstream is automatically destructed:

#include <algorithm>
#include <fstream>
#include <iterator>
#include <vector>

std::string filename("numbersfile");

std::vector<double> myVector{1.342, 16.33, 32.1, 12364};

void write_vector_to_file(const std::vector<double>& myVector, std::string filename);
std::vector<double> read_vector_from_file(std::string filename);

int main()
{
    write_vector_to_file(myVector, filename);
    auto newVector{read_vector_from_file(filename)};
    return 0;
}

void write_vector_to_file(const std::vector<double>& myVector, std::string filename)
{
    std::ofstream ofs(filename, std::ios::out | std::ofstream::binary);
    std::ostream_iterator<double> osi{ofs," "};
    std::copy(myVector.begin(), myVector.end(), osi);
}

std::vector<double> read_vector_from_file(std::string filename)
{
    std::vector<double> newVector{};
    std::ifstream ifs(filename, std::ios::in | std::ifstream::binary);
    std::istream_iterator<double> iter{ifs};
    std::istream_iterator<double> end{};
    std::copy(iter, end, std::back_inserter(newVector));
    return newVector;
}
wally
  • 10,717
  • 5
  • 39
  • 72
  • What if the vector we want to read / write is not of `char` type but `double`? – rivaldo4t Sep 28 '18 at 01:11
  • @rivaldo4t Good question. :) I've updated the last program to show. It seems I should have provided `std::ostream_iterator` with a delimiter and also used `istream_iterator` to read the file. – wally Sep 28 '18 at 19:12
8

There are a number of flaws in your code:

  1. you define a variable named FILE THIS IS BAD BAD BAD. FILE is a name of an already existing object, it's comparable to naming an instance of a vector as: std::vector<int>array{}.
    Not only is it confusing it's extremely dangerous as it will almost certainty lead to naming clashes. Plus, all capitol names should be reserved for macros.

  2. you never check if the file is actually opened, if it isn't the compiler will not warn you and the stream will not give any indication of failure (unless explicitly checked). So, you should always check. The simplest way is too use the streams boolean operator:
    if (!ifile) throw std::runtime_error("error opening file");

  3. you wrote that this doesn't compile:

    std::copy(iter.begin(),iter.end(),std::back_inserter(newVector));

    Why would this work? Iterators themselves don't have begin and end functions, the objects associated with the iterator have those methods.

  4. Piecing all that together here is a modified version of your code:

    {
        std::string path("numbersfile");
    
        std::vector<int> myVector{ 1,16,32,64 };
        std::vector<int> newVector{};
    
    
        std::ofstream outfile(path, std::ios_base::binary);
        std::copy(myVector.begin(), myVector.end(), std::ostreambuf_iterator<char>(outfile));
    
        outfile.close(); 
    
        std::ifstream infile(path,std::ios_base::binary);
        std::istreambuf_iterator<char> iter(infile);
        std::copy(iter, std::istreambuf_iterator<char>(),    std::back_inserter(newVector)); // this leaves newVector empty
    
        infile.close(); // close explicilty for consistency 
    }
    
Community
  • 1
  • 1
Nowhere Man
  • 475
  • 3
  • 9
  • Thanks for the review. I got the naming from the original answer. It is hideous. :) The part that doesn't compile was also from the [accepted answer](http://stackoverflow.com/a/12372783/1460794). Good point suggesting the explicit use of `close` in this context. I tried adding the `throw` you propose (good general practice I agree) but it doesn't address this issue, or rather, it didn't throw when I executed the program on MSVC. – wally Apr 08 '16 at 19:20
  • the throw statement should work, make sure you include the `` header. Add the `if`-`throw` statement after every open. – Nowhere Man Apr 08 '16 at 19:30
  • Nope, didn't work. Also doesn't work [here](http://coliru.stacked-crooked.com/a/90498043343607e1). – wally Apr 08 '16 at 19:35
  • if it's not throwing than that means that the file opened correctly, are you sure it's not opened? There is no reason it shouldn't be working, i tested the solution i gave beforehand and it worked perfectly. – Nowhere Man Apr 08 '16 at 19:40
  • The file opened, but there was no data. – wally Apr 08 '16 at 19:41