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
Returning a Const Reference to an Object Instead of a Copy
Why Would I Prefer Using Vector to Deque
How to Correctly Use Cv::Triangulatepoints()
How to Enable Core Dump in My Linux C++ Program
Why Should I Ever Use Inline Code
How to See the Template Instantiated Code by C++ Compiler
Why Does a C/C++ Compiler Need Know the Size of an Array at Compile Time
Why Doesn't Emplace_Back() Use Uniform Initialization
Differentiate Between Function Overloading and Function Overriding
Function Returning a Lambda Expression
Windows C++ Compiler with Full C++11 Support (Should Work with Qt)
Recursively Iterate Over All the Files in a Directory and Its Subdirectories in Qt
How to Use the Mingw Gdb Debugger to Debug a C++ Program in Windows
C++ Undefined References with Static Library