【问题标题】:Attempting to save vector<int> in bin file and reading it gave random data尝试将 vector<int> 保存在 bin 文件中并读取它会给出随机数据
【发布时间】:2021-12-25 21:13:13
【问题描述】:

我写了两个函数来保存和读取bin文件中的数据:

#include <iostream>
#include <fstream>
#include <vector>

using namespace std;

// save data in file with name p_file
template <typename T>
void save(string p_file, T data) {
    ofstream output(p_file, ios::binary | ios::out);
    output.write((char*) &data, sizeof(data));
    output.close();
}

// read and return data in file with name p_file
template <typename T>
T read(string p_file) {
    ifstream input(p_file, ios::binary | ios::in);
    T data;
    input.seekg(0, input.end);
    int length = input.tellg();
    input.seekg(0, input.beg);
    input.read((char*) &data, length);
    input.close();
    return data;
}

int main() {
    vector<int> vint;
    vint.push_back(1);
    save< vector<int> >("test.bin", vint);
    vector<int> load_vint = read< vector<int> >("test.bin");
    cout << vint[0] << endl;
    cout << load_vint[0] << endl;
    cout << "done" << endl;
}

预期输出:

1
1
done

实际输出:

1
1990048
done

当我将main 中的大部分代码放入test 并且没有更改saveread 中的任何内容时,情况变得更糟了:

#include <iostream>
#include <fstream>
#include <vector>

using namespace std;

// save data in file with name p_file
template <typename T>
void save(string p_file, T data) {
    ofstream output(p_file, ios::binary | ios::out);
    output.write((char*) &data, sizeof(data));
    output.close();
}

// read and return data in file with name p_file
template <typename T>
T read(string p_file) {
    ifstream input(p_file, ios::binary | ios::in);
    T data;
    input.seekg(0, input.end);
    int length = input.tellg();
    input.seekg(0, input.beg);
    input.read((char*) &data, length);
    input.close();
    return data;
}

// exact same code, just in a function
void testing() {
    vector<int> vint;
    vint.push_back(1);
    save< vector<int> >("test.bin", vint);
    vector<int> load_vint = read< vector<int> >("test.bin");
    cout << vint[0] << endl;
    cout << load_vint[0] << endl;
}

int main() {
    testing();
    cout << "done" << endl;
}

预期输出:

1
1
done

实际输出:

1
1924512

发生了什么以及如何解决此错误?

【问题讨论】:

  • 保存例程将vector(通常实现为三个指针)写入文件。这意味着该文件包含三个地址,当您重新读取文件时,这些地址可能无效。如果您写入的数据对于write 这样的简单函数来说太复杂,则需要序列化。
  • 就像@user4581301 指出的那样,vector 并没有真正将数据存储在其中,而是仅包含一些指向数据的指针。作为替代方案,您可以使用std::array,它直接存储数据。
  • 检查我更新的答案。解决方案就在那里,它可以按您的预期工作。

标签: c++ vector fstream


【解决方案1】:

您将向量对象的地址传递给save 函数(位于堆栈上),而不是包含ints 的底层动态数组(位于堆内存上)。还可以看看 std::vector 是如何工作的:https://www.learncpp.com/cpp-tutorial/an-introduction-to-stdvector/

这是我经过大量重构和清理的完整解决方案:

main.cpp


#include <iostream>
#include <fstream>
#include <vector>


// save data in file with name p_file
void save( const std::string& p_file, const std::vector<int>& data )
{
    std::ofstream output( p_file, std::ofstream::binary );

    if ( !output.is_open( ) )
    {
        throw std::ios_base::failure( "Error while opening the file " + p_file );
    }

    for ( auto it = data.begin(); it != data.end(); ++it )
    {
        output.write( reinterpret_cast< const char* >( &(*it) ), sizeof( int ) );
    }

    /*
    if ( !data.empty() )                 // Or use this instead of the for loop
    {
        size_t numOfBytes { data.size( ) * sizeof( int ) };
        output.write( reinterpret_cast< const char* >( &(data[0]) ), numOfBytes );
    }
    */

    output.close();
}

// read and return data in file with name p_file
std::vector<int> read( const std::string& p_file )
{
    std::ifstream input( p_file, std::ifstream::binary );

    if ( !input.is_open( ) )
    {
        throw std::ios_base::failure( "Error while opening the file " + p_file );
    }

    input.seekg( 0, input.end );
    size_t length = input.tellg();
    input.seekg( 0, input.beg );

    size_t numOfIntsInFile { length / sizeof( int ) };

    std::vector<int> data( numOfIntsInFile );

    for ( auto it = data.begin(); it != data.end(); ++it )
    {
        input.read( reinterpret_cast< char* >( &(*it) ), sizeof( int ) );
    }

    /*
    if ( !data.empty() )                  // Or use this instead of the for loop
    {
        input.read( reinterpret_cast< char* >( &(data[0]) ), length );
    }
    */

    input.close();

    return data;
}

// exact same code, just in a function
void test()
{
    std::vector<int> vint;
    vint.push_back( 1 );
    vint.push_back( 2235 );
    vint.push_back( 3 ); // push back as many ints as you want, it won't break.

    std::vector<int> load_vint;

    try
    {
        save( "test.bin", vint );
        load_vint = read( "test.bin" );
    }
    catch ( const std::ios_base::failure& e )
    {
        std::cerr << "Caught an std::ios_base::failure.\n"
                  << e.what() << '\n'
                  << "Error code: " << e.code() << '\n';
    }

    std::cout << '\n';
    std::cout << "Printing the elements of vint: " << '\n';

    for ( const auto& element : vint )
    {
        std::cout << element << '\n';
    }

    std::cout << '\n';
    std::cout << "Printing the elements of load_vint: " << '\n';

    for ( const auto& element : load_vint )
    {
        std::cout << element << '\n';
    }
}

int main()
{
    test();
    std::cout << "\nDone." << std::endl;
}

变化总结:

  1. 我删除了模板,因为它们让我很烦!如果你真的需要模板函数,你可以将它们添加到我的代码中,然后使用它们。

  2. 尽可能使用左值引用(如const std::vector&lt;int&gt;&amp;)以避免不必要的复制。

  3. 尽可能使用 C++ 类型转换(如 reinterpret_cast)而不是 C 风格类型转换。

  4. 在顶部写using namespace std; 避免污染整个源文件。在有限的范围内使用它。

  5. 在处理fstream 对象时使用std::ofstream::binarystd::ifstream::binary 而不是std::ios::binary

  6. 还添加了异常处理机制,以防文件无法打开。

额外说明:清理代码并使其更具可读性可确保其他人可以轻松阅读和理解您的问题。

【讨论】:

  • 有趣的事实:C++ 可以(但很少)在没有堆栈和堆的情况下实现。
  • @user4581301 不是为图灵机设计的吗?没有堆栈它如何工作?
  • 我从来没有见过 C++ 在没有堆栈的情况下在野外实现,但是学者们......他们有时会变得有点奇怪。无论如何,C++ 的抽象模型是如此抽象,以至于它只是概述了自动内存的要求,并让您自己决定如何去做。如果某个巫师能够弄清楚如何使用独角兽角或护理熊填充物来满足所有要求,那么他们将拥有更大的力量。
猜你喜欢
  • 2021-10-06
  • 1970-01-01
  • 2018-05-18
  • 1970-01-01
  • 2015-02-01
  • 1970-01-01
  • 2014-08-14
  • 2016-12-11
  • 1970-01-01
相关资源
最近更新 更多