I need to write a class to a binary file, and then I need to read it back.
I have Triangle and BinaryFile classes, and some other classes. I am not sure if I am writing incorrectly or reading incorrectly. An error occurs when reading. After debugging, I think that it gets inappropriate data for my private variables. I will be very glad if someone can give me some advice on how to make it work properly.
I wasn't sure if I should paste the whole code or not, so I will give you a short snippet of code. Just in case, here is a download link for my source code:
https://my.pcloud.com/publink/show?code=XZJ7CYZbsLWLglqV5p83csijcEUTFqqpM3k
I am a newbie in programming and I don't speak English very well, so I apologize in advance for my mistakes.
class Point
{
private:
int x;
int y;
};
class Figure
{
private:
string name;
string type;
};
class Triangle: public Figure
{
private:
Point p1, p2, p3;
};
class BinaryFile
{
private:
string FileName;
fstream File;
public:
//...
void AddNewFigure(istream& stream)
{
File.open(this->FileName, ios::binary | ios::app);
if(!this->File)
{
cerr<<"File error <"<<this->FileName<<">\n";
exit(1);
}
Triangle fig;
fig.MakeNewFigure(stream);
File.write((char*)&fig, sizeof(Triangle));
File.close();
}
Triangle GetTriangle()
{
Triangle trig;
Point p;
string str(""); int x(0);
File.open(this->FileName, ios::binary | ios::in);
if(!this->File)
{
cerr<<"File error <"<<this->FileName<<">\n";
exit(1);
}
File.read((char*)&trig, sizeof(Triangle));
File.close();
return trig;
}
};
The answer depends on whether you are just doing this to learn how files work or whether saving to the file is just incidental and you don't care how it works.
If you just want to get the stuff to save and restore and you don't care how it works then use a third party library. There are many many of them.
If you want to learn how to read and write things to files then you will need to make your own read and write functions. I have made a sample program that will explain how it works:
#include <string>
#include <fstream>
#include <iostream>
class Point
{
private:
int x;
int y;
public:
Point():x(0),y(0){}
Point(int x,int y):x(x),y(y){}
void write(std::ostream& f)
{
// We can just write out the bytes for x and y because
// they are primitive types stored in the class
f.write( (char*)&x, sizeof(x) );
f.write( (char*)&y, sizeof(y) );
}
void read(std::istream& f)
{
// We can just read the bytes directly into x and y because
// they are primitive types stored in the class
f.read( (char*)&x, sizeof(x) );
f.read( (char*)&y, sizeof(y) );
}
};
class Figure
{
private:
std::string name;
std::string type;
public:
Figure(){}
Figure(std::string name,std::string type):name(name),type(type){}
void write(std::ostream& f)
{
size_t size;
// we need to store the data from the string along with the size
// because to restore it we need to temporarily read it somewhere
// before storing it in the std::string (istream::read() doesn't
// read directly to std::string)
size = name.size();
f.write( (char*)&size, sizeof(size_t) );
f.write( (char*)name.c_str(), size );
size = type.size();
f.write( (char*)&size, sizeof(size_t) );
f.write( (char*)type.c_str(), size );
}
void read(std::istream& f)
{
size_t size;
char *data;
// when we read the string data we need somewhere to store it
// because we std::string isn't a primitive type. So we read
// the size, allocate an array, read the data into the array,
// load the std::string, and delete the array
f.read( (char*)&size, sizeof(size) );
data = new char[size+1];
f.read( data, size );
data[size]='\0';
name = data;
delete data;
f.read( (char*)&size, sizeof(size) );
data = new char[size+1];
f.read( data, size );
data[size]='\0';
type = data;
delete data;
}
};
class Triangle: public Figure
{
private:
Point p1, p2, p3;
public:
Triangle(){}
Triangle(Point x,Point y,Point z,Figure f):p1(x),p2(y),p3(z),Figure(f){}
void write(std::ostream& f)
{
// First write the base class then write the members of this class
Figure::write(f);
p1.write(f);
p2.write(f);
p3.write(f);
}
void read(std::istream& f)
{
// First read the base class then read the members of this class
Figure::read(f);
p1.read(f);
p2.read(f);
p3.read(f);
}
};
class BinaryFile
{
private:
std::string FileName;
std::fstream File;
public:
BinaryFile(std::string FileName) : FileName(FileName) {};
void WriteTriangle()
{
File.open(FileName, std::ios::binary | std::ios::out);
if(!File)
{
std::cerr<<"File error <"<<FileName<<">\n";
exit(1);
}
Triangle trig({1,2},{3,4},{5,6},{"name","type"}); // something new
trig.write(File);
File.close();
}
Triangle ReadTriangle()
{
File.open(FileName, std::ios::binary | std::ios::in);
if(!File)
{
std::cerr<<"File error <"<<FileName<<">\n";
exit(1);
}
Triangle trig; // default values
trig.read(File);
File.close();
return trig;
}
};
main()
{
BinaryFile bin("file.bin");
bin.WriteTriangle();
Triangle trig = bin.ReadTriangle();
// at this point trig has the values we stored
return 0;
}
It's not easy to reproduce the error, due to your large source code and missing data file. But a quick inspection shows that you read and write the binary data using bloc operations:
Triangle trig;
...
File.read((char*)&trig, sizeof(Triangle));
Unfortunately this kind of approach only works if the object you want to save/load is of a class that is trivially copyable, as the following code will demonstrate:
if (is_trivially_copyable<Triangle>::value)
cout << "Triangle is trivially copyable" << endl;
else cout << "Triangle is not trivially copyable" << endl;
So you'll have to serialize the object content writing field by field instead of using a bloc operation. This FAQ on serialization should help you to consider the alternatives.
What you are looking for is to serialize your classes/data that should be saved to file. There are several libraries that has been optimized regarding time and memory consumption for this. Would you mind using a 3rd party library?
If not, have a look at for example boost serialization, cereal or maybe even Google's ProtoBuf. I recon Cereal is a good start if you are using C++11.
If you'd like to write your own serialization you'd have to consider that for every object that has a dynamic size (such as a string), you will also need to save the object's size to the file. For more info please have a look here:
https://stackoverflow.com/a/11003590/5874704
Related
I am new to C++ and I am trying to output a wave file in. I have had success with binary files in both C# and Java but I am not yet comfortable with C++ yet. I know about that arrays and objects should generally be created on the heap.
It is fine with the strings and the first getter
Whenever it gets to the second getter for the base class it runs out of memory.
This class is called waveWriter and it extends a class called WaveFormat that contains the getters
WaveWriter header:
class WaveWriter : WaveFormat {
private:
std::string fileName;
public:
WaveWriter(uint16_t channels, uint32_t sampleRate,
uint16_t bitsPerSample, double* lSampleData,
uint32_t lSampleLength, double* rSampleData,
uint32_t rSampleLength, bool isFloat, std::string outputName);
~WaveWriter();
void writeWave() {
std::ofstream myFile;
myFile = std::ofstream(fileName, std::ios::out | std::ios::binary);
// write the header
// sGroupID
myFile << S_GROUP_ID_HEADER;
// dwfilelength
myFile.write(reinterpret_cast<const char *> (GetDwFileLength()),
sizeof (GetDwFileLength()));
// sRiffType
myFile << S_RIFF_TYPE;
// write the format
// sGroupID
myFile << S_GROUP_ID_FORMAT;
// dwChunkSize
myFile.write(reinterpret_cast<const char *> (GetDwFormatChunkSize()),
sizeof (GetDwFormatChunkSize()));
// wFormatTag
........ blah blah blah
// close file
myFile.close();
return;
}
};
cpp for this class:
WaveWriter::WaveWriter(uint16_t channels, uint32_t sampleRate,
uint16_t bitsPerSample, double* lSampleData,
uint32_t lSampleLength, double* rSampleData,
uint32_t rSampleLength, bool isFloat, std::string outputName) :
WaveFormat(channels, sampleRate, bitsPerSample,
lSampleData, lSampleLength, rSampleData,
rSampleLength, isFloat) {
outputName.append(".wav");
this->fileName = outputName;
}
WaveWriter::~WaveWriter() {
this->~WaveFormat();
}
Header for WaveFormat contains private variables a constructor and getters like these to access the private variables:
public:
uint16_t GetCbSize() {
return cbSize;
}
uint32_t GetDwAvgBytesPerSec() {
return dwAvgBytesPerSec;
}
uint32_t GetDwChannelMask() {
return dwChannelMask;
}......
This is speculation based on your functions name, but I think this code:
myFile.write(reinterpret_cast<const char *> (GetDwFileLength()),sizeof (GetDwFileLength()));
is incorrect. Assuming GetDwFileLength() return size as value, it is incorrect to cast it to const char *. You need to save it in another argument and post its address to cast. Something like this:
auto val = GetDwFileLength();
myFile.write(reinterpret_cast<const char *> (&val), sizeof (val));
I see similar mistake several times in your code. This mistake can make invalid memory access.
In addition you should use virtual base destructor rather than calling base destructor from derived class. Never call base destructor in derived class.
I have written one c++ class which has read and write methods. These methods read and write row from/to csv file.
Currently I have implemented read and write for csv file having five tuples(columns), say - EmpNo, EmpName, Address, Department, Manager.
Class contains these 5 tuples as member variables of class.
So basically, in read(), I am reading row using fstream and putting tuple values in to respective member variable. Similarly, for write I am getting row data from user into class member variable and writing the same in csv file.
Now I want to use same code for reading and writing another csv file having only two tuples out of above five tuples- EmpNo, EmpName.
I can think of maintaining one variable to identify which CSV I am reading/writing and accordingly have if/else in all code. But this doesn't look cleaner approach.
pseudo code for read() with my approach is as below:
read()
{
read EmpNo;
read EmpName;
If (csv_with_5_tuple == true)
{
read Address;
read Department;
read Manager;
}
}
//Here, 'csv_with_5_tuple ' will be set when reading/writing from/to csv file of five tuples.
With this approach, I need to add 'if' condition everywhere in class.
Can anyone suggest me the best way to do this in c++?
You can use class inheritance for this. There are pseudo-code demonstrating the idea:
class csv2 {
public:
virtual void read()
{
read EmpNo;
read EmpName;
}
};
class csv5 : public csv2
{
public:
virtual void read()
{
csv2::read();
read Address;
read Department;
read Manager;
}
};
By using some vector and template, you might do:
template <typename T>
std::vector<T> csv_read(std::istream& is, const std::vector<std::string T::*>& members)
{
std::vector<T> res;
std::string header;
std::getline(is, header);
while (true) {
T obj;
for (auto m : members) {
is >> obj.*m;
}
if (!is) {
break;
}
res.push_back(obj);
}
return res;
}
With usage similar to
const std::vector<std::string Person2::*> members = {&Person2::Name, &Person2::AgeStr};
auto persons = csv_read<Person2>(ss, members);
Demo
Or simpler if you just use std::vector<std::vector<std::string>>:
std::vector<std::vector<std::string>> csv_read(std::istream& is, std::size_t rowCount)
{
std::vector<std::vector<std::string>> res;
std::string header;
std::getline(is, header);
while (true) {
std::vector<std::string> row(rowCount);
for (auto& col : row) {
is >> col;
}
if (!is) {
break;
}
res.push_back(row);
}
return res;
}
With usage similar to
auto data = csv_read(ss, 2);
Demo
I have two classes that will represent two very simple databases, and each has a "Save" function which will write what's in the class to a file. Since the code within the "Save" function is very similar, I was wondering if I could factor it out.
One of my colleagues said this might be possible with inheritance and/or metadata, so I tried looking into it myself with Google. However, I couldn't find anything that was helpful and am still unsure if what I want to do is even possible.
If it's possible to factor out, then I think I'd need to have another class or function know about each class's types and iterate through them somehow (metadata?). It would check the type of every data, and depending on what the type is, it would make sure that it's correctly output to the text file.
(I know data like name, age, etc. should be private, but to keep this simple I just had everything be public)
class A
{
public:
A() : name(""), age(0) {};
void Save(void)
{
std::string filename = "A.txt";
std::string data;
data += name + "\n";
data += std::to_string(age) + "\n";
std::ofstream outfile(filename);
outfile.write(data.c_str(), data.size());
outfile.close();
}
std::string name;
int age;
};
class B
{
public:
B() : ID(0), points(0) {};
void Save(void)
{
std::string filename = "B.txt";
std::string data;
data += std::to_string(ID) + "\n";
data += std::to_string(points) + "\n";
std::ofstream outfile(filename);
outfile.write(data.c_str(), data.size());
outfile.close();
}
int ID;
int points;
};
int main(void)
{
A a;
B b;
a.name = "Bob"; a.age = 20;
b.ID = 4; b.points = 95;
a.Save();
b.Save();
return 0;
}
A possible solution could be to use metaprogramming (not sure what you mean by metadata), i.e. templates to reuse the common parts
template<typename T1, typename T2>
void TSave(const std::string fname, const T1& p1, const T2& p2) {
std::string filename = fname;
std::stringstream data;
data << p1 << "\n";
data << p2 << "\n";
std::ofstream outfile(filename);
outfile.write(data.str().c_str(), data.str().size());
outfile.close();
}
class A {
...
void Save(void) {
TSave("A.txt", name, age);
}
std::string name;
int age;
};
class B {
...
void Save(void) {
TSave("B.txt", ID, points);
}
int ID;
int points;
};
Live Example
What you are looking for is serialization: saving objects to a file (and one day or another, restore the objects).
Of course, you could write your own serialization framework, and Marco's answer is an interesting start in that direction. But alternatively, you could consider existing libraries, such as boost::serialization :
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
class A {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & name;
ar & age;
}
...
};
class B {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & ID;
ar & points;
}
...
};
main() {
A a;
B b;
...
{
std::ofstream ofs("myfile");
boost::archive::text_oarchive arch(ofs);
arch << a << b;
}
}
As you see, it's still needed to say what's to be written to the file. However, the code is simplified : you don't have to worry about file management and transformation of data. And it works also with standard containers.
You won't find a C++ trick that automatically determines for a class what's to be saved. Two reasons for that:
C++ allows metaprogramming, but it is not reflexive: there are no standard process to find out at execution time which members compose a class.
In an object, some data can be transient, i.e. it means only something at the time of the execution and depends on the context. For example pointers: you could save the value of a pointer to a file, but it will mean nothing when you reload it later (the pointer is only valid until you free the object). The proper way would be to save the object that is pointed to (but where, when, how?).
I have class Writer that has two ofstream members.
Both streams are associated with the same output file. I'd like to use both streams in Writer::write method, but to make sure that each stream writes to the end of the real output file.
Code
class my_ofstream1:
public ofstream
{
// implement some functions.
// using internal, extended type of streambuf
};
class my_ofstream2:
public ofstream
{
// implement some functions.
// using internal, extended type of streambuf
// (not the same type as found in my_ofstream1)
};
class Writer
{
public:
void open(string path)
{
f1.open(path.c_str(),ios_base::out); f2.open(path.c_str(),ios_base::out);
}
void close()
{
f1.close(); f2.close();
}
void write()
{
string s1 = "some string 1";
string s2 = "some string 2";
f1.write(s1.c_str(), s1.size());
// TBD - ensure stream f2 writes to the end of the actual output file
assert(f1.tellp() == f2.tellp());
f2.write(s2.c_str(), s2.size());
}
private:
my_ofstream1 f1;
my_ofstream1 f2;
};
void main()
{
Writer w;
w.open("some_file.txt");
w.write();
w.close();
}
Questions
How to ensure f2 is in sync with f1? meaning, before writing, stream offset of f2 must be in sync with stream offset of f1 and vice versa?
I can't use function std::ios::rdbuf since each ofstream uses special derived streambuf. so by using rdbuf() I'll lose the necessary functionality.
I tried using some of the techniques found in Synchronizing Streams topic but could not make it happen.
This looks like both of your classes use the filtering streambuf
idiom. In any case, don't derive your classes from
std::ofstream, but directly from ostream, and have them both
use the same std::filebuf object. If you are using the
filtering streambuf idiom, don't let your filtering
streambuf's buffer; leave that to the final std::filebuf.
In other words, your "internal, extended type of streambuf"
should contain a pointer to the final sink, which will be
a filebuf (but your filtering streambufs don't need to know
this). Functions like sync, they just pass on to the final
destination, and they should never establish a buffer
themselves, but pass everything on to the filebuf.
Is this not what you are looking for? This could be easily modified to work with ostreams rather the ofstreams, which is nicer - the actual issue is synchronisation of the buffers. In this code I have simply made the filebuf bf unbuffered and it works fine. Alternatively leave it buffered but include calls to pubsync when switching between my_ofstream's. I don't understand why ios:rdbuf is not available. Are you creating your own streambuf?
#include <iostream>
#include <fstream>
#include <assert.h>
using namespace std;
class my_ofstream1 : public ofstream
{
public:
my_ofstream1& write (const char_type* s, streamsize n)
{
ofstream::write (s, n);
//rdbuf()->pubsync();
return *this;
}
void attach (filebuf* bf){
ios::rdbuf(bf);
}
};
class my_ofstream2 : public ofstream
{
public:
my_ofstream2& write (const char_type* s, streamsize n)
{
ofstream::write (s, n);
//rdbuf()->pubsync();
return *this;
}
void attach (filebuf* bf){
ios::rdbuf(bf);
}
};
class Writer
{
filebuf bf;
my_ofstream1 f1;
my_ofstream1 f2;
public:
void open(string path)
{
bf.open(path.c_str(),ios_base::out);
bf.pubsetbuf(0,0); //unbufferred
f1.attach(&bf); f2.attach(&bf);
}
void close()
{
f1.close(); f2.close();
}
void write()
{
string s1 = "some string 1";
string s2 = "some string 2";
f1.write(s1.c_str(), s1.size());
assert(f1.tellp() == f2.tellp());
f2.write(s2.c_str(), s2.size());
}
};
int main()
{
Writer w;
w.open("some_file.txt");
w.write();
w.close();
return 0;
}
I want to write a structure which has a list of integer id. The list can be of varying length.
typedef struct ss_iidx_node {
int totalFreq;
vector < int > docIDList;
}s_iidx_node;
Now, I wish to write this structure in a file and read it back.
How can I do it?
Wrting is done:
fwrite(&obj,sizeof(s_iidx_node),1,dat_fd2);
When I read it back, it gives garbage value. It seems it storing only the strating and ending position of stl vector...which on reading is garbage?
Any idea how to do it
Thanks
Your code is simply non-portable. It tries to treat object as a raw sequence of bytes, which is plainly undefined for non-POD objects in the C++ standard (and your struct is non-POD because it contains a member of a non-POD type std::vector).
What happens in practice is that vector class typically consists of 3 fields: pointer to beginning of data, size, and capacity. What you see are bytes constituting those values written into the file.
You should consider avoiding C-style file I/O entirely, and using C++ streams and Boost Serialization library instead - it supports STL collections out of the box.
Though I'd rather see an approach based on an explicit serialisation, you could try:
fwrite(&obj.totalFreq,sizeof(int),1,dat_fd2);
fwrite(&obj.docIDList[0],sizeof(int),obj.totalFreq,dat_fd2);
Assuming totalFreq == docIDList.size(), it's a spurious variable, so a better implementation would be:
size_t size=obj.docIDList.size();
fwrite(&size,sizeof(size_t),1,dat_fd2);
fwrite(&obj.docIDList[0],sizeof(int),size,dat_fd2);
My preferred implementation would be:
size_t size=obj.docIDList.size();
fwrite(&size,sizeof(size_t),1,dat_fd2);
for (size_t i=0;i<size;i++)
{
int id=obj.docIDList[i];
fwrite(&id,sizeof(int),1,dat_fd2);
}
The vector class is defined roughly like this:
template <typename T>
class vector {
...
T* array; // pointer to the actual data, stored in a dynamically allocated array
size_t arrayLength;
...
};
The actual data of the vector are stored in a dynamically allocated array. The vector class simply holds a pointer to that array. So your fwrite call only stores the contents of the vector class, not the contents of the array it points to.
You need to write out the actual elements of the vector instead.
I tried this on VS2010 Beta1. Did not try on other compilers. Please check out.
class Employee
{
private:
int _empno;
string _name;
public:
Employee(int empno, string name) : _empno(empno), _name(name) { }
Employee() : _empno(-1), _name("") { }
virtual ~Employee() { }
virtual int GetEmpId() const;
virtual string GetName() const;
friend ostream& operator<<(ostream& os, const Employee& emp);
};
class Manager : public Employee
{
private:
vector<Employee> Reportees;
public:
Manager() : Employee() { }
Manager(int empno, const string& name) : Employee(empno, name) { }
~Manager() { }
void InsertEmployees(const Employee& emp);
friend ostream& operator<<(ostream& os, const Manager& manger);
};
void Manager::InsertEmployees(const Employee& emp)
{
Reportees.push_back(emp);
}
ostream& operator<<(ostream& os, const Manager& manager)
{
os << "Empid:" << manager.GetEmpId()
<< "|Name:" << manager.GetName() << endl;
typedef vector<Employee>::const_iterator EmpIter;
EmpIter iter = manager.Reportees.begin();<br>
for ( ; iter != manager.Reportees.end(); ++iter)
{
Employee e = *iter;
os << "Reportee" << endl;
os << "Empid:" << e.GetEmpId()
<< "|Name:" << e.GetName() << endl;
}
return os;
}
int main()
{
ofstream data("data.txt");
ofstream bin_data("data.bin", ios::binary);
Employee e1(100, "Jagan");
Employee e2(101, "Nath");
Employee e3(102, "Sai");
Employee e4(103, "Pantula");
Manager m(104, "Neeraja");
m.InsertEmployees(e1);
m.InsertEmployees(e2);
m.InsertEmployees(e3);
m.InsertEmployees(e4);
data << m;
data.close();
bin_data.write(reinterpret_cast<char*>(&m), sizeof(m));
bin_data.close();
ReadDataFromFile();
bin_data.close();
}
void ReadDataFromFile()
{
ifstream bin_data("data.bin", ios::binary);
Manager m;
while (bin_data.read(reinterpret_cast<char*>(&m), sizeof(m)))
cout << m;
}