正确地使用迭代器将std::vector读写到文件中

6
我正在尝试理解这里提供的答案,但似乎无法使其正常工作。这里是答案链接。
以下是我的尝试:
#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
}

copy 后,newVector 仍然为空。如何更新最后一条语句以填充 newVector


4
在第一个 std::copy 后面加上 FILE.flush(); - Piotr Skotnicki
2个回答

13
第二次调用copy时文件还没有准备好读取。(感谢Piotr Skotnicki在评论中的回答) 调用flush可以使程序正常工作:
#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;
}

ofstream在创建ifstream时仍然处于作用域内。如果此时调用了ofstream的析构函数,那么文件也将准备好供ifstream使用。在下面的程序中,ifstream被自动销毁:

#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;
}

如果我们想要读取/写入的向量不是char类型而是double类型,该怎么办? - rivaldo4t
@rivaldo4t 很好的问题。 :) 我已经更新了最后一个程序来展示。看起来我应该提供带有分隔符的 std::ostream_iterator 并且使用 istream_iterator 来读取文件。 - wally

8

你的代码存在一些问题:

  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 
    }
    

感谢您的评论。我从原始答案中得到了命名,它很丑陋 :) 无法编译的部分也来自已接受的答案。在这种情况下,明确使用 close 的建议很好。我尝试添加您建议的 throw(我同意这是一个好的一般做法),但它并没有解决这个问题,或者说,在我在 MSVC 上执行程序时,它没有抛出异常。 - wally
抛出语句应该能够正常工作,确保包含了<exception>头文件。在每个开放语句后添加if-throw语句。 - Nowhere Man
不行,没用。在这里也不行:http://coliru.stacked-crooked.com/a/90498043343607e1。 - wally
如果没有抛出异常,那就意味着文件已经成功打开了,你确定它没有被打开吗?没有理由它不工作,我之前测试过给出的解决方案,完美地运行了。 - Nowhere Man
文件已打开,但没有数据。 - wally

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接