I'm making my OOP final project and part of it is to make a couple of databases, I'm having trouble with this one.
Every time an Alquiler x is made, it needs to go to the database's vector<Alquiler>, and then stored in a binary file. Problem is that I'm probably managing the sizes wrong because once I add a couple of Alquiler, the array gets filled up with rubbish, always outputting random characters.
Here's the code, I tried to compact it as much as possible and just leave the relevant part. If anyone wants to take a look I'd really appreciate it.
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
using namespace std;
struct Date
{
int day, month, year;
};
struct structalquiler
{
int dni, totalq, dep;
int faday, famonth, fayear;
int fdday, fdmonth, fdyear;
vector<string> artlist;
};
class Alquiler {
private:
int a_DNI_Cliente;
int a_Total_Alquiler, a_Deposito;
Date a_Date_Alquiler, a_Date_Devolucion;
vector<string> a_Article_list;
public:
Alquiler ();
Alquiler (int dni, int totalq, int dep, Date fa, Date fd, vector<string> arts);
void save(ofstream &outfile) { // Bin file writing
structalquiler reg;
reg.dni = a_DNI_Cliente;
reg.dep = a_Deposito;
reg.totalq = a_Total_Alquiler;
reg.faday = a_Date_Alquiler.day;
reg.famonth = a_Date_Alquiler.month;
reg.fayear = a_Date_Alquiler.year;
reg.fdday = a_Date_Devolucion.day;
reg.fdmonth = a_Date_Devolucion.month;
reg.fdyear = a_Date_Devolucion.year;
reg.artlist = a_Article_list;
int wo_size = sizeof(reg) - sizeof(reg.artlist);
outfile.write((char*)®, wo_size);
int size = reg.artlist.size(); // Writing of the vector<string>
outfile.write((char*)&size, sizeof(int));
for (int i = 0; i < size; i++)
{
int letter_amount = reg.artlist[i].length();
outfile.write((char*)&letter_amount, sizeof(int));
outfile.write(reg.artlist[i].c_str(), letter_amount);
}
}
void read(ifstream &infile) { // Reading of the file
structalquiler reg;
int wo_size = sizeof(reg) - sizeof(reg.artlist);
infile.read((char*)®, wo_size);
a_DNI_Cliente = reg.dni;
a_Deposito = reg.dep;
a_Total_Alquiler = reg.totalq;
a_Date_Alquiler.day = reg.faday;
a_Date_Alquiler.month = reg.famonth;
a_Date_Alquiler.year = reg.fayear;
a_Date_Devolucion.day = reg.fdday;
a_Date_Devolucion.month = reg.fdmonth;
a_Date_Devolucion.year = reg.fdyear;
// a_Article_list = reg.artlist;
vector<string> v; // Reading of the vector<string>
int read_size;
infile.read((char*)&read_size, sizeof(int));
for (int i = 0; i < read_size; i++)
{
int letter_amount;
infile.read((char*)&letter_amount, sizeof(int));
char* buffer = new char[letter_amount + 1];
infile.read(buffer, letter_amount);
buffer[letter_amount] = '\0';
v.push_back(string(buffer));
delete [] buffer;
}
a_Article_list = v;
}
};
Alquiler::Alquiler(){}
Alquiler::Alquiler(int dni, int totalq, int dep, Date fa, Date fd, vector<string> arts)
: a_DNI_Cliente(dni), a_Total_Alquiler(totalq), a_Deposito(dep), a_Date_Alquiler(fa), a_Date_Devolucion(fd), a_Article_list(arts) {}
class Database {
private:
string m_file_name = "alquileres.dat";
vector<Alquiler> Arreglo_Alquileres;
public:
Database() { // storing in the vector<Alquiler>
ifstream infile(m_file_name.c_str(), ios::binary|ios::ate);
if (infile.is_open())
{
int size = infile.tellg();
int cant = size / sizeof(structalquiler);
Arreglo_Alquileres.resize(cant);
infile.seekg(0);
for (int i = 0; i < cant; i++)
{
Arreglo_Alquileres[i].read(infile);
}
infile.close();
}
}
void add(const Alquiler &x) {
Arreglo_Alquileres.push_back(x);
write();
}
bool write() {
ofstream outfile(m_file_name.c_str(), ios::binary|ios::trunc);
if (!outfile.is_open()) return false;
int cant = Arreglo_Alquileres.size();
for (int i = 0; i < cant; i++)
{
Arreglo_Alquileres[i].save(outfile);
}
outfile.close();
return true;
}
};
int main() {
Database base;
vector<string> v = {"panuelo", "alpargatas", "chaleco"};
Alquiler x(9,8,7,{6,5,4},{3,2,1},v);
base.add(x);
return 0;
}
Any help is appreciated.
I used a debugger to check your code as is step by step. And then I used a hex-editor to show the output.
And the result is: Your code, as shown, works fine.
But, if you use longer strings, then the std::vector in the structalquiler will have a variable length.
If you now want to read back the data, then you cannot calculate the number of elements from the filesize.
Example for Hex file after running the program as shown one time:
You can see that there is a padding of one int after the 9 data ints. But assuming that the program runs in the same environment, there should be no problem.
And this codes works fine. So, running the program 3 times it will give:
But now: What if we have longer string, then, we could get a file like this:
So, int cant = size / sizeof(structalquiler); will not work any longer.
The solution is to read the file, record for record, until all data from the file will be consumed.
I will show you a quick fix, but you should consider a redesign.
The following will work:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
using namespace std;
struct Date
{
int day, month, year;
};
struct structalquiler
{
int dni, totalq, dep;
int faday, famonth, fayear;
int fdday, fdmonth, fdyear;
vector<string> artlist;
};
class Alquiler {
private:
int a_DNI_Cliente;
int a_Total_Alquiler, a_Deposito;
Date a_Date_Alquiler, a_Date_Devolucion;
vector<string> a_Article_list;
public:
Alquiler();
Alquiler(int dni, int totalq, int dep, Date fa, Date fd, vector<string> arts);
void save(ofstream& outfile) { // Bin file writing
structalquiler reg;
reg.dni = a_DNI_Cliente;
reg.dep = a_Deposito;
reg.totalq = a_Total_Alquiler;
reg.faday = a_Date_Alquiler.day;
reg.famonth = a_Date_Alquiler.month;
reg.fayear = a_Date_Alquiler.year;
reg.fdday = a_Date_Devolucion.day;
reg.fdmonth = a_Date_Devolucion.month;
reg.fdyear = a_Date_Devolucion.year;
reg.artlist = a_Article_list;
int sss1 = sizeof(reg);
int sss2 = sizeof(reg.artlist);
int wo_size = sizeof(reg) - sizeof(reg.artlist);
outfile.write((char*)®, wo_size);
int size = reg.artlist.size(); // Writing of the vector<string>
outfile.write((char*)&size, sizeof(int));
for (int i = 0; i < size; i++)
{
int letter_amount = reg.artlist[i].length();
outfile.write((char*)&letter_amount, sizeof(int));
outfile.write(reg.artlist[i].c_str(), letter_amount);
}
}
void read(ifstream& infile) { // Reading of the file
structalquiler reg;
int wo_size = sizeof(reg) - sizeof(reg.artlist);
if (infile.read((char*)®, wo_size)) {
a_DNI_Cliente = reg.dni;
a_Deposito = reg.dep;
a_Total_Alquiler = reg.totalq;
a_Date_Alquiler.day = reg.faday;
a_Date_Alquiler.month = reg.famonth;
a_Date_Alquiler.year = reg.fayear;
a_Date_Devolucion.day = reg.fdday;
a_Date_Devolucion.month = reg.fdmonth;
a_Date_Devolucion.year = reg.fdyear;
// a_Article_list = reg.artlist;
vector<string> v; // Reading of the vector<string>
int read_size;
infile.read((char*)&read_size, sizeof(int));
for (int i = 0; i < read_size; i++)
{
int letter_amount;
infile.read((char*)&letter_amount, sizeof(int));
char* buffer = new char[letter_amount + 1];
infile.read(buffer, letter_amount);
buffer[letter_amount] = '\0';
v.push_back(string(buffer));
delete[] buffer;
}
a_Article_list = v;
}
}
};
Alquiler::Alquiler() {}
Alquiler::Alquiler(int dni, int totalq, int dep, Date fa, Date fd, vector<string> arts)
: a_DNI_Cliente(dni), a_Total_Alquiler(totalq), a_Deposito(dep), a_Date_Alquiler(fa), a_Date_Devolucion(fd), a_Article_list(arts) {}
class Database {
private:
string m_file_name = "r:\\alquileres.dat";
vector<Alquiler> Arreglo_Alquileres;
public:
Database() { // storing in the vector<Alquiler>
ifstream infile(m_file_name.c_str(), ios::binary | ios::ate);
if (infile.is_open())
{
int size = infile.tellg();
int ss2 = sizeof(structalquiler);
int cant = size / sizeof(structalquiler);
//Arreglo_Alquileres.resize(cant);
infile.seekg(0);
while (infile) {
Arreglo_Alquileres.push_back({});
Arreglo_Alquileres.back().read(infile);
}
if (not infile) Arreglo_Alquileres.pop_back();
infile.close();
}
}
void add(const Alquiler& x) {
Arreglo_Alquileres.push_back(x);
write();
}
bool write() {
ofstream outfile(m_file_name.c_str(), ios::binary | ios::trunc);
if (!outfile.is_open()) return false;
int cant = Arreglo_Alquileres.size();
for (int i = 0; i < cant; i++)
{
Arreglo_Alquileres[i].save(outfile);
}
outfile.close();
return true;
}
};
int main() {
Database base;
vector<string> v = { "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "alpargatas", "chaleco" };
Alquiler x(9, 8, 7, { 6,5,4 }, { 3,2,1 }, v);
base.add(x);
return 0;
}
For example if my file name is : sum00.in, sum.00.out, is there a possibility excepting the one of writing each time the same piece of code:
ifstream in("sum00.in");
ofstream out("sum00.out");
I first thought of macro:
#define read(x) ifstream in("x.in"); ofstream out("x.out"); ///But it doesn`t work for a word.
Every advice that would help me writing the same piece of could would be helpful!
I would recommend not using a macro. I'd encapsulate it in a class.
Example:
#include <fstream>
#include <iostream>
#include <string>
class MyIO {
public:
explicit MyIO(const std::string& basename) :
in(basename + ".in"),
out(basename + ".out")
{}
std::ifstream& is() { return in; }
std::ofstream& os() { return out; }
private:
std::ifstream in;
std::ofstream out;
};
int main() {
MyIO f("sum001"); // open both sum001.in and sum001.out
int i;
int sum = 0;
while(f.is() >> i) { // read from the ifstream
sum += i;
}
f.os() << sum << '\n'; // write to the ofstream
}
My program uses ifstream() and getline() to parse a text file in to objects that are two vectors deep. i.e vector inside vector. The inner vector contains over 250000 string objects once the text file is finished loading.
this is painfully slow. Is there an STD alternative that is more efficient than using ifstream() and getline() ?
Thanks
UPDATE:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <regex>
using namespace std;
class Word
{
private:
string moniker = "";
vector <string> definition;
string type = "";
public:
void setMoniker(string m) { this->moniker = m; }
void setDefinition(string d) { this->definition.push_back(d); }
void setType(string t) { this->type = t; }
int getDefinitionSize() { return this->definition.size(); }
string getMoniker() { return this->moniker; }
void printDefinition()
{
for (int i = 0; i < definition.size(); i++)
{
cout << definition[i] << endl;
}
}
string getType() { return this->type; }
};
class Dictionary
{
private:
vector<Word> Words;
public:
void addWord(Word w) { this->Words.push_back(w); }
Word getWord(int i) { return this->Words[i]; }
int getTotalNumberOfWords() { return this->Words.size(); }
void loadDictionary(string f)
{
const regex _IS_DEF("[\.]|[\ ]"),
_IS_TYPE("^misc$|^n$|^adj$|^v$|^adv$|^prep$|^pn$|^n_and_v$"),
_IS_NEWLINE("\n");
string line;
ifstream dict(f);
string m, t, d = "";
while (dict.is_open())
{
while (getline(dict, line))
{
if (regex_search(line, _IS_DEF))
{
d = line;
}
else if (regex_search(line, _IS_TYPE))
{
t = line;
}
else if (!(line == ""))
{
m = line;
}
else
{
Word w;
w.setMoniker(m);
w.setType(t);
w.setDefinition(d);
this->addWord(w);
}
}
dict.close();
}
}
};
int main()
{
Dictionary dictionary;
dictionary.loadDictionary("dictionary.txt");
return 0;
}
You should reduce your memory allocations. Having a vector of vectors is usually not a good idea, because every inner vector does its own new and delete.
You should reserve() the approximate number of elements you need in the vector at the start.
You should use fgets() if you don't actually need to extract std::string to get your work done. For example if the objects can be parsed from char arrays, do that. Make sure to read into the same string buffer every time, rather than creating new buffers.
And most important of all, use a profiler.
This question already has answers here:
Need help regarding saving variables via fstream, do I need to use vector?
(2 answers)
Closed 8 years ago.
I got this problem, where I want to make a Devicelist, which I do with a vector of Devices. But I can't make it read/write correctly.
This is my functions:
#include "Devicelist.h"
bool Devicelist::AddDevice(const char *deviceName, char *type)
{
Device tempDevice(deviceName, type, ++id_);
Devicelist_.push_back(tempDevice);
return true;
}
bool Devicelist::deleteDevice(const char *deviceName)
{
for (int i = 0; i < Devicelist_.size(); i++)
{
if (strcmp(deviceName, Devicelist_[i].getName()) == 0)
{
Devicelist_.erase(Devicelist_.begin() + i);
return true;
}
else
{
cout << "No Device found with that Devicename." << endl;
return false;
}
}
}
bool Devicelist::SaveToFile()
{
//remove("Devices.dat");
ofstream SaveFile("Devices.dat", ios::out | ios::binary);
if (!SaveFile)
{
cerr << "File could not be opened." << endl;
return false;
}
for (int i = 0; i < Devicelist_.size(); i++)
SaveFile.write((const char *)(&Devicelist_[i]), sizeof(Devicelist_[i]));
SaveFile.close();
return true;
}
bool Devicelist::LoadFromFile()
{
ifstream LoadFile("Devices.dat", ios::in | ios::binary);
if (!LoadFile)
{
cerr << "File could not be opened." << endl;
return false;
}
for (int i = 0; i < Devicelist_.size(); i++)
LoadFile.read((char *)(&Devicelist_[i]), sizeof(Devicelist_[i]));
LoadFile.close();
return true;
}
Device Devicelist::findDevice(const char *deviceName)
{
for (int i = 0; i < Devicelist_.size(); i++)
{
if (strcmp(deviceName, Devicelist_[i].getName()) == 0)
return Devicelist_[i];
else
cout << "Couldn't find device." << endl;
}
}
And this is my main:
#include "Devicelist.h"
void main()
{
Devicelist list;
list.AddDevice("Lampe3", "Lampe");
list.AddDevice("Lampe4", "Lampe");
list.SaveToFile();
Devicelist list2;
list2.LoadFromFile();
Device lampe = list2.findDevice("Lampe");
cout << lampe.getName() << endl;
cout << lampe.getID() << endl;
cout << lampe.getType() << endl;
}
Can anyone see what my problem is?
Thanks in advance!
Edit1:
My .h-file for Devicelist is:
#ifndef DEVICELIST_H
#define DEVICELIST_H
#include "Device.h"
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
class Devicelist
{
public:
Devicelist();
bool SaveToFile();
bool LoadFromFile();
bool AddDevice(const char *deviceName, char *type);
bool deleteDevice(const char *deviceName);
Device findDevice(const char *devicename);
private:
vector<Device> Devicelist_;
int id_;
};
#endif
This is my Device .h-file:
#ifndef DEVICE_H
#define DEVICE_H
#include <iostream>
#include <string>
#define DNAME_SIZE 33
using namespace std;
class Device
{
public:
Device(const char *devicename = "Default", char *type = "type", int id = 0);
const char *getName();
int getID();
int getType();
private:
char deviceName_[DNAME_SIZE];
int id_;
int type_;
};
#endif
And Device .cpp-file:
#include "Device.h"
Device::Device(const char *deviceName, char *type, int id)
{
strncpy_s(deviceName_, deviceName, DNAME_SIZE);
if (type == "Lampe")
type_ = 1;
else if (type == "Roegalarm")
type_ = 2;
else if (type == "Tyverialarm")
type_ = 3;
else
{
type_ = 0;
cout << "Type does not exists." << endl;
}
id_ = id;
}
const char *Device::getName()
{
return deviceName_;
}
int Device::getID()
{
return id_;
}
int Device::getType()
{
return type_;
}
The problem is when I try to read view what's saved in the file, this is the output:
http://imgur.com/P8WEAKq
The problem is here in the LoadFromFile function, I think:
for (int i = 0; i < Devicelist_.size(); i++)
LoadFile.read((char *)(&Devicelist_[i]), sizeof(Devicelist_[i]));
The DeviceList is empty, or has the wrong size.
You could write something similar as in AddDevice:
Device tempDevice();
LoadFile.read((char *)(&tempDevice), sizeof(tempDevice));
Devicelist_.push_back(tempDevice);
but the size (how many devices) of the file needs to be known.
As mentioned in a comment, this works only if Device doesn't have pointers to other objects for example. It should be a POD (Plain Old Data) object.
An elegant solution would be to implement streaming operators on Device
You will need to to have Device::StoreToFile write the number of devices before writing out each device. This will enable your Device::LoadFromFile to know how many device instances to read from the file.
I highly recommend you do not write a class or structure bit for bit to the file. If your class or struct has any advanced data structures, such as std::string or std::vector, you can't write them in binary. They may use pointers to memory, which will not be the same when you load them.
Here is a better solution:
1. Pass a buffer of uint8_t to the load and store methods.
2. The methods load from the buffer or store into the buffer (append).
3. The caller reads or writes the buffer to file.
4. The class also has a "size on file" method that returns the space occupied on a file, which may be different than the structure size.
Also search the web for "boost::serialization".
I have a problem. When I compile the program I don't have any errors, but when I use valgrind:
Uninitialized value was created by a heap allocation (line with new)
Conditional jump or move depends on uninitialized value(s)(line with delete)
I search through the forums however I didn't find much information which could help me.
I would be really grateful for a hint.
My program
#include <cstdlib>
#include <stdint.h>
#include <cstdio>
#include <iostream>
#include <string>
#include <istream>
#include <cstring>
#include <fstream>
#include <sstream>
#include <cctype>
using namespace std;
using std::cout;
using std::endl;
int dlugosc,miejsce;
ifstream file;
class channel
{
public:
int start;
double length;
int bytespix;
int resolution;
channel(double g) : start(g),
length(0),
bytespix(0),
resolution(0)
{
}
};
int fileopen() // opens the file and returns its size
{
file.open ("0_dlc.000", ios::in|ios::binary);
if( file.good() == true )
{
cout << "Uzyskano dostep do pliku!" << endl;
}
else
{
cout<< "File cannot open" <<endl;
}
file.seekg(0, file.end);
dlugosc = file.tellg();
return dlugosc;
}
int findword(const char* slowo,int startplace)
{
int m;
int c=0;
int cur=0;
unsigned int equal=0;
char element=0;
file.seekg (startplace, file.beg);
for(m=0;m<dlugosc;m++)
{
file.get(element);
if(element==slowo[cur])
{
equal++;
cur++;
}
else
{
equal=0;
cur=0;
if(element==slowo[cur])
{
equal++;
cur++;
}
}
if(equal==strlen(slowo))
{
return m+startplace;
}
}
return 0;
}
int findvalue(const char* wartosc,int startpoint)
{
int p;
int g;
char element=0;
char* buffer = new char[9];
miejsce = findword(wartosc,startpoint); // miejsce to global variable
file.seekg (miejsce+1, file.beg);
for(p=0;(int)element<58;p++)
{
file.get(element);
if((int)element>58 || (int)element<48)
break;
else
buffer[p] = element;
}
buffer[p]='\0';
g = atoi(buffer);
delete [] buffer;
return g;
}
int main()
{
int a,h=0,channels,start=0,length=0,resolution=0,bytespix=0,m=0;
const char* slowko="Data offset: ";
dlugosc=fileopen();
channel** kanaly=0;
kanaly = new channel*[9];
miejsce=0;
for(a=0;a<9;a++)
{
kanaly[a] = new channel(4);
start = findvalue("Data offset: ",miejsce+20);
kanaly[a]->start=start;
}
for(m=0;m<9;m++)
{
delete kanaly[m];
}
delete []kanaly;
file.close();
}
The problem is in the constructor of channel. Initialize all member variables, and the problem will go away :
class channel
{
public:
double start;
double length;
int bytespix;
int resolution;
channel(double g) : start(g),
length(0),
bytespix(0),
resolution(0)
{
}
};