试图在 bin 文件中保存 vector<int> 并读取它给出了随机数据
Attempting to save vector<int> in bin file and reading it gave random data
我写了两个函数来保存和读取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
并且没有更改 save
和 read
中的任何内容时,情况变得更糟:
#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
这是怎么回事,我该如何解决这个错误?
您正在将向量对象的地址传递给 save
函数(位于堆栈中)而不是包含 int
的底层动态数组(位于堆内存中)秒。另请查看 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;
}
改动总结:
我删除了模板,因为它们让我很烦!如果你真的需要模板函数,你可以将它们添加到我的代码中,然后使用它们。
尽可能使用左值引用(如const std::vector<int>&
)以避免不必要的复制。
尽可能使用 C++ 转换(如 reinterpret_cast
)而不是 C 风格的转换。
通过在顶部写入 [=16=] 来避免污染整个源文件。在有限的范围内使用它。
在处理 fstream
对象时使用 std::ofstream::binary
或 std::ifstream::binary
而不是 std::ios::binary
。
还添加了异常处理机制以防无法打开文件。
额外注意:清理代码并使其更具可读性可确保其他人可以轻松阅读和理解您的问题。
我写了两个函数来保存和读取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
并且没有更改 save
和 read
中的任何内容时,情况变得更糟:
#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
这是怎么回事,我该如何解决这个错误?
您正在将向量对象的地址传递给 save
函数(位于堆栈中)而不是包含 int
的底层动态数组(位于堆内存中)秒。另请查看 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;
}
改动总结:
我删除了模板,因为它们让我很烦!如果你真的需要模板函数,你可以将它们添加到我的代码中,然后使用它们。
尽可能使用左值引用(如
const std::vector<int>&
)以避免不必要的复制。尽可能使用 C++ 转换(如
reinterpret_cast
)而不是 C 风格的转换。通过在顶部写入 [=16=] 来避免污染整个源文件。在有限的范围内使用它。
在处理
fstream
对象时使用std::ofstream::binary
或std::ifstream::binary
而不是std::ios::binary
。还添加了异常处理机制以防无法打开文件。
额外注意:清理代码并使其更具可读性可确保其他人可以轻松阅读和理解您的问题。