So i have a struct called info that contains different kind of variable and i created a vector inside of this struct. now i want to save this vector into a binary file so i can read it later. My code so far
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
struct MyStruct
{
int a;
};
struct info
{
char name[30];
int age;
char bbl[20];
std::vector<MyStruct> my;
};
void create(std::vector<info>& test)
{
std::ofstream file("info.dat", std::ios::binary | std::ios::app); // output file stream
// dump the contents of the vector in the file
for (const info& inf : test)
file.write(reinterpret_cast<const char*>(&inf), sizeof(inf));
}
int main()
{
info info1;
// create a test vector
std::vector<info> test;
info1.age = 3443;
std::cin >> info1.name;
std::cin >> info1.bbl;
MyStruct my;
my.a = 4;
info1.my.push_back(my);
test.push_back(info1);
std::cout << '\n';
create(test);
test.clear(); // clear the vector
{
std::ifstream file("info.dat", std::ios::binary); // input file stream
// read the contents of the file into the vector
info inf;
while (file.read(reinterpret_cast<char*>(&inf), sizeof(inf)))
test.push_back(inf);
}
// print out the contents of test
for (int i = 0; i < test.size(); i )
std::cout << "{ " << test[i].name << ", " << test[i].age << ", " << test[i].bbl << " } ";
std::cout << '\n';
}
But i got error on while (file.read(reinterpret_cast<char*>(&inf), sizeof(inf)))
which is
Exception thrown at 0x7AD63E9E (vcruntime140d.dll) in ConsoleApplication9.exe: 0xC0000005: Access violation reading location 0xCC007364.
How can i fix that?
CodePudding user response:
The std::vector
object itself probably does not contain any actual data. Instead, it probably only contains bookkeeping information, for example
- the number of valid elements,
- the maximum number of elements for which memory has been allocated, and
- a pointer to the start of the actual data.
Therefore, simply dumping the contents of the std::vector
object to file will not be useful.
If you want to write the actual data of a std::vector
to file, you must first decide how it should be stored in the file. One simple way of storing it would be to first write the number of elements as an int
to the file, and then to write all of the individual elements to the file one after another. That way, when reading the file later, the reading function can easily find out how many elements it should read from the file, simply by reading the first value.
You may want to change your function create
to the following:
void create(std::vector<info>& test)
{
std::ofstream file("info.dat", std::ios::binary );
for ( const info& inf : test )
{
//write "name" member to file
file.write( reinterpret_cast<const char*>(&inf.name), sizeof inf.name );
//write "age" member to file
file.write( reinterpret_cast<const char*>(&inf.age), sizeof inf.age );
//write "bbl" member to file
file.write( reinterpret_cast<const char*>(&inf.bbl), sizeof inf.bbl );
//write "my" member to file, giving it special treatment,
//because it is a std::vector
{
int num_elements = inf.my.size();
//write number of elements to file
file.write( reinterpret_cast<const char*>(&num_elements), sizeof num_elements );
//write the individual elements to file
for ( int i = 0; i < num_elements; i )
{
file.write( reinterpret_cast<const char*>(&inf.my[i]), sizeof inf.my[i] );
}
}
}
//verify that stream is still in a good state
if ( !file )
throw std::runtime_error( "output error" );
}
Note that I removed std::ios::app
in the code above, because it did not seem appropriate for what you are doing.
For reading the file contents, you can now use the following function:
void read(std::vector<info>& test)
{
std::ifstream file("info.dat", std::ios::binary );
info inf;
//try reading new entries from file until end-of-file or error occurs
for (;;)
{
//read "name" member from file
file.read( reinterpret_cast<char*>(&inf.name), sizeof inf.name );
//read "age" member from file
file.read( reinterpret_cast<char*>(&inf.age), sizeof inf.age );
//read "bbl" member from file
file.read( reinterpret_cast<char*>(&inf.bbl), sizeof inf.bbl );
//read "my" member from file, giving it special treatment,
//because it is a std::vector
{
int num_elements;
//read number of elements from file
file.read( reinterpret_cast<char*>(&num_elements), sizeof num_elements );
//don't start loop if loop counter is invalid
if ( !file )
break;
//read the individual elements from file
for ( int i = 0; i < num_elements; i )
{
MyStruct my;
file.read( reinterpret_cast<char*>(&my), sizeof my );
if ( file )
inf.my.push_back( my );
else
break;
}
//stop main loop if data was not successfully read
if ( !file )
break;
test.push_back( inf );
}
}
}
Your entire program will now look like this:
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
struct MyStruct
{
int a;
};
struct info
{
char name[30];
int age;
char bbl[20];
std::vector<MyStruct> my;
};
void create(std::vector<info>& test)
{
std::ofstream file("info.dat", std::ios::binary );
for ( const info& inf : test )
{
//write "name" member to file
file.write( reinterpret_cast<const char*>(&inf.name), sizeof inf.name );
//write "age" member to file
file.write( reinterpret_cast<const char*>(&inf.age), sizeof inf.age );
//write "bbl" member to file
file.write( reinterpret_cast<const char*>(&inf.bbl), sizeof inf.bbl );
//write "my" member to file, giving it special treatment,
//because it is a std::vector
{
int num_elements = inf.my.size();
//write number of elements to file
file.write( reinterpret_cast<const char*>(&num_elements), sizeof num_elements );
//write the individual elements to file
for ( int i = 0; i < num_elements; i )
{
file.write( reinterpret_cast<const char*>(&inf.my[i]), sizeof inf.my[i] );
}
}
}
//verify that stream is still in a good state
if ( !file )
throw std::runtime_error( "output error" );
}
void read(std::vector<info>& test)
{
std::ifstream file("info.dat", std::ios::binary );
info inf;
//try reading new entries from file until end-of-file or error occurs
for (;;)
{
//read "name" member from file
file.read( reinterpret_cast<char*>(&inf.name), sizeof inf.name );
//read "age" member from file
file.read( reinterpret_cast<char*>(&inf.age), sizeof inf.age );
//read "bbl" member from file
file.read( reinterpret_cast<char*>(&inf.bbl), sizeof inf.bbl );
//read "my" member from file, giving it special treatment,
//because it is a std::vector
{
int num_elements;
//read number of elements from file
file.read( reinterpret_cast<char*>(&num_elements), sizeof num_elements );
//don't start loop if loop counter is invalid
if ( !file )
break;
//read the individual elements from file
for ( int i = 0; i < num_elements; i )
{
MyStruct my;
file.read( reinterpret_cast<char*>(&my), sizeof my );
if ( file )
inf.my.push_back( my );
else
break;
}
//stop main loop if data was not successfully read
if ( !file )
break;
test.push_back( inf );
}
}
}
int main()
{
info info1;
// create a test vector
std::vector<info> test;
info1.age = 3443;
std::cin >> info1.name;
std::cin >> info1.bbl;
MyStruct my;
my.a = 4;
info1.my.push_back(my);
test.push_back(info1);
std::cout << '\n';
create(test);
test.clear(); // clear the vector
read( test );
// print out the contents of test
for (int i = 0; i < test.size(); i )
std::cout << "{ " << test[i].name << ", " << test[i].age << ", " << test[i].bbl << " } ";
std::cout << '\n';
}
This program has the following output:
TestName
TestBBL
{ TestName, 3443, TestBBL }
As you can see, the written data was read back properly.
CodePudding user response:
You can read/write vector in binary, or vector of structure, as long as the struct is POD. The whole data can be obtained from vec.data()
and the size of data is vec.size() * sizeof(vec[0])
In your example, the structure is not POD, this method can't be used. But the structure can be broken down in to POD sub vector, and we can read/write each individual element.
Note however that this method is OS dependent. The binary data can be stored in big-endian or little-endian format, the structure size may vary between compilers. The file is not portable, at least not without additional work.
(This code requires C 17)
#include <iostream>
#include <fstream>
#include <vector>
struct info_pod
{ char name[30]; int age; char bbl[20]; };
struct info : info_pod
{ std::vector<int> subvec; };
int main()
{
std::vector<info> src{
{ "name1", 100, "bbl1", { {10}, {11}, {12} } },
{ "name2", 101, "bbl2", { {10}, {11}, {12} } },
{ "name3", 102, "bbl3", { {10}, {11}, {12} } }
};
std::ofstream fout("info.dat", std::ios::binary);
if (!fout)
return 0;
for (auto& e : src)
{
fout.write((char*)&e, sizeof(info_pod));
size_t count = e.subvec.size();
fout.write((char*)&count, sizeof(count));
auto vecsize = count * sizeof(e.subvec[0]);
fout.write((char*)e.subvec.data(), vecsize);
}
fout.close();
//read it back
std::ifstream fin("info.dat", std::ios::binary);
if (!fin)
return 0;
std::vector<info> dst;
while (true)
{
info inf;
fin.read((char*)&inf, sizeof(info_pod));
if (fin.gcount() != sizeof(info_pod)) break;
size_t count;
fin.read((char*)&count, sizeof(count));
if (fin.gcount() != sizeof(count)) break;
inf.subvec.resize(count);
auto vecsize = count * sizeof(inf.subvec[0]);
fin.read((char*)inf.subvec.data(), vecsize);
if (fin.gcount() != vecsize) break;
dst.push_back(inf);
}
for (auto& e : dst)
{
std::cout << e.name << ',' << e.age << ',' << e.bbl << " MyStruct: ";
for (auto& i : e.subvec) std::cout << i << ',';
std::cout << '\n';
}
}