Reading and Writing a Std::Vector into a File Correctly

Reading and writing a std::vector into a file correctly

Try using an ostream_iterator/ostreambuf_iterator, istream_iterator/istreambuf_iterator, and the STL copy methods:

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

#include <fstream> // looks like we need this too (edit by π)

std::string path("/some/path/here");

const int DIM = 6;
int array[DIM] = {1,2,3,4,5,6};
std::vector<int> myVector(array, array + DIM);
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 iter(INFILE);
std::copy(iter.begin(), iter.end(), std::back_inserter(newVector));

Reading and writing a std::vector into a file correctly with iterators

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

Reading and writing C++ vector to a file

As Laurynas says, std::vector is guaranteed to be contiguous, so that should work, but it is potentially non-portable.

On most systems, sizeof(Vertex) will be 12, but it's not uncommon for the struct to be padded, so that sizeof(Vertex) == 16. If you were to write the data on one system and then read that file in on another, there's no guarantee that it will work correctly.

Reading and writing a std::vectorint to / from registry with WriteProfileBinary

Since I know my values will be under 256 I have decided to stick with BYTE for the container.

Writing to Registry

std::vector<BYTE> vExcludedColumns;

// POpulate vector

UINT uSize = gsl::narrow<UINT>(sizeof(BYTE) * vExcludedColumns.size());
theApp.WriteProfileBinary(
strSection,
_T("AssignStatesEx"),
vExcludedColumns.data(),
uSize
);
theApp.WriteProfileInt(strSection, _T("AssignStatesExSize"), uSize);

Reading from Registry

std::vector<BYTE> vExcludedColumns;
UINT uSize = theApp.GetProfileInt(strSection, _T("AssignStatesExSize"), 0);
UINT uSizeRead = 0;
BYTE* temp = nullptr;
theApp.GetProfileBinary(strSection, _T("AssignStatesEx"), &temp, &uSizeRead);
if (uSizeRead == uSize)
{
vExcludedColumns.resize(uSizeRead, 0);
memcpy(vExcludedColumns.data(), temp, uSizeRead);
}
delete[] temp;
temp = nullptr;

I believe that this will still work for both 32 bit and 64 bit.

I am open to comments if this code can be improved or simplified.



Updated

Here is the same code, but added into public app methods:

std::vector<BYTE> CMeetingScheduleAssistantApp::GetProfileVector(CString strSection, CString strKey)
{
std::vector<BYTE> vData;
UINT uSize = theApp.GetProfileInt(strSection, strKey + _T("Size"), 0);
UINT uSizeRead = 0;
BYTE* temp = nullptr;
theApp.GetProfileBinary(strSection, strKey, &temp, &uSizeRead);
if (uSizeRead == uSize)
{
vData.resize(uSizeRead, 0);
memcpy(vData.data(), temp, uSizeRead);
}
delete[] temp;
temp = nullptr;
return vData;
}

void CMeetingScheduleAssistantApp::WriteProfileVector(CString strSection, CString strKey, std::vector<BYTE> vData)
{
UINT uSize = gsl::narrow<UINT>(sizeof(BYTE) * vData.size());
theApp.WriteProfileBinary(
strSection,
strKey,
vData.data(),
uSize
);
theApp.WriteProfileInt(strSection, strKey + _T("Size"), uSize);
}

Read/write a vector of Structs into a binary file and reading a vector of structs from a file in C++

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.

How to read a file into vector in C++?

Your loop is wrong:

for (int i=0; i=((Main.size())-1); i++) {

Try this:

for (int i=0; i < Main.size(); i++) {

Also, a more idiomatic way of reading numbers into a vector and writing them to stdout is something along these lines:

#include <iostream>
#include <iterator>
#include <fstream>
#include <vector>
#include <algorithm> // for std::copy

int main()
{
std::ifstream is("numbers.txt");
std::istream_iterator<double> start(is), end;
std::vector<double> numbers(start, end);
std::cout << "Read " << numbers.size() << " numbers" << std::endl;

// print the numbers to stdout
std::cout << "numbers read in:\n";
std::copy(numbers.begin(), numbers.end(),
std::ostream_iterator<double>(std::cout, " "));
std::cout << std::endl;

}

although you should check the status of the ifstream for read errors.

c++, how to write several simple vectors to a binary file in one shot

The trick to reading and writing the whole data section of a vector is getting the size of the data block right before you write it, and then getting the size of the data block right BEFORE you read it.But of course, you don't know the size when you read it, so the size needs to be in the file also. That allows you to allocate that much space for your vector, then read that much data.

Here is one possible implementation. I took shortcuts. Your file header should probably have an identifier you can check so you know that you are reading a file that claims to follow your layout correctly. You really, really need to check to see if the file open and reads/writes work. And I didn't write a test for my operator==(), which I used to test the load/save pair (although I did inspect the values in the debugger once).

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <fstream>

struct Vectors {
std::vector<int> A, B, C, D, E, F;
bool save(const char * filename);
bool load(const char * filename);
bool operator == (const Vectors &rhs);
};

void initialize_dummy_ints(std::vector<int> &v, int size){
v.resize(size);
for (int n = 0; n < size; ++n)
v[n] = n + 1;
}

bool Vectors::save(const char * filename){
std::ofstream out(filename, std::ios::binary);
int a=A.size(), b=B.size(), c=C.size(), d=D.size(), e=E.size(), f=F.size();
out.write(reinterpret_cast<const char*>(&a), sizeof(a));
out.write(reinterpret_cast<const char*>(&b), sizeof(b));
out.write(reinterpret_cast<const char*>(&c), sizeof(c));
out.write(reinterpret_cast<const char*>(&d), sizeof(d));
out.write(reinterpret_cast<const char*>(&e), sizeof(e));
out.write(reinterpret_cast<const char*>(&f), sizeof(f));

out.write(reinterpret_cast<const char*>(&A[0]), sizeof(int)*A.size());
out.write(reinterpret_cast<const char*>(&B[0]), sizeof(int)*B.size());
out.write(reinterpret_cast<const char*>(&C[0]), sizeof(int)*C.size());
out.write(reinterpret_cast<const char*>(&D[0]), sizeof(int)*D.size());
out.write(reinterpret_cast<const char*>(&E[0]), sizeof(int)*E.size());
out.write(reinterpret_cast<const char*>(&F[0]), sizeof(int)*F.size());

// always check to see if the file opened, and if writes succeeded.
// I am being lazy here so I can focus on the actual question
return true;
}

bool Vectors::load(const char *filename){
std::ifstream in(filename, std::ios::binary);
int a, b, c, d, e, f;
in.read(reinterpret_cast<char*>(&a), sizeof(a));
in.read(reinterpret_cast<char*>(&b), sizeof(b));
in.read(reinterpret_cast<char*>(&c), sizeof(c));
in.read(reinterpret_cast<char*>(&d), sizeof(d));
in.read(reinterpret_cast<char*>(&e), sizeof(e));
in.read(reinterpret_cast<char*>(&f), sizeof(f));
A.resize(a); B.resize(b); C.resize(c); D.resize(d); E.resize(e); F.resize(f);

in.read(reinterpret_cast<char*>(&A[0]), sizeof(int)*A.size());
in.read(reinterpret_cast<char*>(&B[0]), sizeof(int)*B.size());
in.read(reinterpret_cast<char*>(&C[0]), sizeof(int)*C.size());
in.read(reinterpret_cast<char*>(&D[0]), sizeof(int)*D.size());
in.read(reinterpret_cast<char*>(&E[0]), sizeof(int)*E.size());
in.read(reinterpret_cast<char*>(&F[0]), sizeof(int)*F.size());

// always check to see if the file opened, and if writes succeeded.
// I am being lazy here so I can focus on the actual question
return true;
}

bool matches(const std::vector<int> &l, const std::vector<int> &r){
if (l.size() != r.size())
return false;
for (size_t x = 0; x < l.size(); ++x)
if (l[x] != r[x])
return false;
return true;
}

bool Vectors::operator==(const Vectors &rhs){
return matches(A, rhs.A) && matches(B, rhs.B) && matches(C, rhs.C) && matches(D, rhs.D) && matches(E, rhs.E) && matches(F, rhs.F);
}

int main()
{
// setup
Vectors starting_values;
initialize_dummy_ints(starting_values.A, 10);
initialize_dummy_ints(starting_values.B, 12);
initialize_dummy_ints(starting_values.C, 14);
initialize_dummy_ints(starting_values.D, 10);
initialize_dummy_ints(starting_values.E, 5);
initialize_dummy_ints(starting_values.F, 2);

// write to file
starting_values.save("data.bin");

// read back in to memory
Vectors loaded_values;
loaded_values.load("data.bin");

// compare
if (loaded_values == starting_values)
std::cout << "success";
else
std::cout << "failure";

return 0;
}

Why isn't my method of saving a vector to a file and then reading it back out again working?

While the implementation of std::vector can differ between implementations, they all are a variant of the following (highly simplified):

template <typename T>
struct vector {
T* storage;
int size;
};

In other words, the bit pattern that makes up a std::vector in memory does not contain the actual contents only a pointer to it. You will need to implement an serialization format (sometimes called marshalling) for vectors yourself.

You could, for example, use protocol buffers or cap'n proto.



Related Topics



Leave a reply



Submit