To explain briefly: Using <fstream>, I write a std::list instance to a .txt file:
#include <fstream>
#include <list>
std::list<Item> list_1; //example list
list_1.push_back(Item(...));
std::ofstream file;
file.open("record.txt", std::ios::trunc);
if (file.is_open()) {
file.write((char*)&list_1, sizeof(std::list<Item>)) << std::endl;
file.close();
}
However, when I read from the same file and assign the data to a std::list instance:
file.open("record.txt", std::ios::in);
if (file.is_open()) {
std::list<Item> list_1;
file.read((char*)&list_1, sizeof(std::list<Item>));
}
It gives me an error when I try to access its elements. This is, however, not my problem. Because std::list stores the pointer to that element, I must store the elements manually, like I did here:
for (auto const& item : list_1) {
file << item.amount << std::endl;
file << item.value << std::endl;
file << item.item_name << std::endl;
file << (char*)&item.type << std::endl;
}
Then I read these values. Use the values to create a new Item instance and store it inside my list. Side note: I can access the size() property of the list_1 from the .txt file because it is a member of std::list<Item> which lives on the stack. So it gets saved by the ofstream.
for (int i = 0; i < list_1.size(); i++) {
int amount = 0;
int value = 0;
std::string item_name;
Item_Type type = item;
file >> amount;
file >> value;
file >> item_name;
file >> (char*)&type;
Item item(amount, value, item_name, type);
main_player_inv.push_back(item);
I expect this to work, because now the std::list should have no uninitialized members, right?
Well, it gives me this error:
this->_Mypair._Myval2._Myhead was 0x228B4050240
This basically means list_1->_Mypair._Myval2._Myhead is a pointer which points to memory out of bounds. The problem is, unlike the element pointers which I can manually save the values of and initialize, I can't access the data of list_1->_Mypair._Myval2._Myhead or edit it manually, as it is a private member. Or, there isn't a way I could find online.
So, I have two questions:
Can I initialize list_1->_Mypair._Myval2._Myhead so that it points to a valid memory?
Is there a way to more easily serialize a std::list and retrieve it's content?
If both of these questions are unanswerable, I would like to talk about what I'm trying to do:
The std::list<Item> is used as a character or an object's inventory. In my project, I want to store the items the player and objects such as containers have in a std::list<Item> instance. I thought this was the most fitting thing to do for an object-oriented Player structure. Here are some classes, for example:
Player class
class Player : public Object {
public:
int level, experience;
double health;
float resistance; // 0.000 - 1.000
std::list<Item> inventory;
public:
Player() :
level(0), experience(0), health(10.0), resistance(0.0f) {};
Player(int x, int y, std::string obj_name, Obj_Type type, int level, int experience, double health, float resistence) :
Object(x, y, obj_name, type), level(level), experience(experience), health(health), resistance(resistence) {};
};
Item class
struct Item {
public:
unsigned int amount, value;
std::string item_name;
Item_Type type; // enum
public:
Item() :
amount(0), value(0), item_name("undefined"), type(item) {};
Item(int amount, int value, std::string item_name, Item_Type type) :
amount(amount), value(value), item_name(item_name), type(type) {};
};
If you know a better way to store player items, items being class instances; or know altogether a better way to do this, please help me.
You can't read/write the raw bytes of a std::list object (or any other non-trivial type), as you would be writing/reading raw pointers and other internal data members that you don't need to concern yourself with.
You must (de)serialize your class's individual data members instead, as you have already discovered.
I would suggest a binary format instead of a textual format, eg:
#include <type_traits>
template <typename T, std::enable_if_t<std::is_scalar<T>::value, bool> = true>
void writeToStream(std::ostream &out, const T &value) {
out.write(reinterpret_cast<const char*>(&value), sizeof(value));
}
template <typename T, std::enable_if_t<std::is_scalar<T>::value, bool> = true>
void readFromStream(std::istream &in, T &value) {
in.read(reinterpret_cast<char*>(&value), sizeof(value));
}
void writeToStream(std::ostream &out, const std::string &value) {
size_t size = value.size();
writeToStream(out, size);
out.write(value.c_str(), size);
}
void readFromStream(std::istream &in, std::string &value) {
size_t size;
readFromStream(in, size);
value.resize(size);
in.read(value.data() /* or: &value[0] */, size);
}
template <typename Container>
void writeToStream(std::ostream &out, const Container &items) {
size_t count = items.size();
writeToStream(out, count);
for(const auto& item : items) {
writeToStream(out, item);
}
}
template <typename Container>
void readFromStream(std::istream &in, Container &items) {
size_t count;
readFromStream(in, count);
items.reserve(count);
for(size_t i = 0; i < count; ++i) {
Container::value_type item;
readFromStream(in, item);
items.push_back(item);
}
}
template<typename Container>
void writeToFile(const std::string &fileName, const Container &items) {
std::ofstream file(fileName, std::ios::binary);
file.exceptions(std::ofstream::failbit);
writeToStream(file, items);
}
template<typename Container>
void readFromFile(const std::string &fileName, Container &items) {
std::ifstream file(fileName, std::ios::binary);
file.exceptions(std::ifstream::failbit);
readFromStream(file, items);
}
struct Item {
public:
unsigned int amount, value;
std::string item_name;
Item_Type type; // enum
public:
Item() :
amount(0), value(0), item_name("undefined"), type(item) {};
Item(int amount, int value, std::string item_name, Item_Type type) :
amount(amount), value(value), item_name(item_name), type(type) {};
void writeTo(std::ostream &out) const {
writeToStream(out, amount);
writeToStream(out, value);
writeToStream(out, item_name);
writeToStream(out, type);
}
void readFrom(std::istream &in) {
readFromStream(in, amount);
readFromStream(in, value);
readFromStream(in, item_name);
readFromStream(in, type);
}
};
void writeToStream(std::ostream &out, const Item &item) {
item.writeTo(out);
}
void readFromStream(std::istream &in, Item &item) {
item.readFrom(in);
}
class Player : public Object {
public:
int level, experience;
double health;
float resistance; // 0.000 - 1.000
std::list<Item> inventory;
public:
Player() :
level(0), experience(0), health(10.0), resistance(0.0f) {};
Player(int x, int y, std::string obj_name, Obj_Type type, int level, int experience, double health, float resistence) :
Object(x, y, obj_name, type), level(level), experience(experience), health(health), resistance(resistence) {};
void writeTo(std::ostream &out) const {
writeToStream(out, level);
writeToStream(out, experience);
writeToStream(out, health);
writeToStream(out, resistance);
writeToStream(out, inventory);
}
void readFrom(std::istream &in) {
readFromStream(in, level);
readFromStream(in, experience);
readFromStream(in, health);
readFromStream(in, resistance);
readFromStream(in, inventory);
}
};
void writeToStream(std::ostream &out, const Player &player) {
player.writeTo(out);
}
void readFromStream(std::istream &in, Player &player) {
player.readFrom(in);
}
#include <fstream>
#include <list>
int main() {
std::list<Item> list_1; //example list
list_1.push_back(Item(...));
writeToFile("record.txt", list_1);
list_1.clear();
readFromFile("record.txt", list_1);
return 0;
}
If you really want a textual file, then use operator<< and operator>> instead, overriding them in your classes, eg:
(feel free to tweak this to use whatever formatting you want...)
#include <limits>
void discardLine(std::istream &in) {
in.ignore(std::numeeric_limits<std::streamsize>::max(), '\n');
}
template<typename CharT, typename Traits>
void streamFailed(std::basic_ios<CharT,Traits> &stream) {
stream.setstate(std::ios_base::failbit);
}
template <typename Container>
std::ostream& operator<<(std::ostream &out, const Container &items) {
out << '[' << items.size() << '\n';
for(const auto& item : items) {
out << item << '\n';
}
out << ']\n';
return out;
}
template <typename Container>
std::istream& operator>>(std::istream &in, Container &items) {
char ch;
in >> ch;
if (ch != '[') {
streamFailed(in);
} else {
size_t count;
in >> count;
discardLine(in);
items.reserve(count);
for(size_t i = 0; i < count; ++i) {
Container::value_type item;
in >> item;
items.push_back(item);
}
in >> ch;
if (ch != '[') {
streamFailed(in);
}
}
}
template<typename Container>
void writeToFile(const std::string &fileName, const Container &items) {
std::ofstream file(fileName, std::ios::binary);
file.exceptions(std::ofstream::failbit);
file << items;
}
template<typename Container>
void readFromFile(const std::string &fileName, Container &items) {
std::ifstream file(fileName, std::ios::binary);
file.exceptions(std::ifstream::failbit);
file >> items;
}
struct Item {
public:
unsigned int amount, value;
std::string item_name;
Item_Type type; // enum
public:
Item() :
amount(0), value(0), item_name("undefined"), type(item) {};
Item(int amount, int value, std::string item_name, Item_Type type) :
amount(amount), value(value), item_name(item_name), type(type) {};
friend std::ostream& operator<<(std::ostream &out, const Item &item) {
out << '(' << item.amount << ' ' << item.value << ' ' << static_cast<int>(item.type) << ' ' << item.item_name << ')';
return out;
}
friend std::istream& operator>>(std::istream &in, Item &item) {
char ch;
in >> ch;
if (ch != '(') {
streamFailed(in);
} else {
int itype;
in >> item.amount >> item.value >> itype;
item.type = static_cast<Item_Type>(itype);
std::getline(in >> std::ws, item_name, ')');
}
return in;
}
};
class Player : public Object {
public:
int level, experience;
double health;
float resistance; // 0.000 - 1.000
std::list<Item> inventory;
public:
Player() :
level(0), experience(0), health(10.0), resistance(0.0f) {};
Player(int x, int y, std::string obj_name, Obj_Type type, int level, int experience, double health, float resistence) :
Object(x, y, obj_name, type), level(level), experience(experience), health(health), resistance(resistence) {};
friend std::ostream& operator<<(std::ostream &out, const Player &player) {
out << '(' << level << ' ' << experience << ' ' health << ' ' << resistance << '\n';
out << inventory;
out << ')';
return out;
}
friend std::istream& operator>>(std::istream &in, Player &player) {
char ch;
in >> ch;
if (ch != '(') {
streamFailed(in);
} else {
in >> player.level >> player.experience >> player.health >> player.resistance >> player.inventory;
in >> ch;
if (ch != ')') {
streamFailed(in);
}
}
return in;
}
};
#include <fstream>
#include <list>
int main() {
std::list<Item> list_1; //example list
list_1.push_back(Item(...));
writeToFile("record.txt", list_1);
list_1.clear();
readFromFile("record.txt", list_1);
return 0;
}
I have a homework task where I should complete the body of a function located in a separate file Find.h which should be completed in such a way that the code written below should compile successfully:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include "Find.h"
using namespace std;
class Company {
std::string name;
int id;
public:
std::string getName() const {
return this->name;
}
int getId() const {
return this->id;
}
friend std::istream& operator>>(std::istream& stream, Company& company);
};
std::istream& operator>>(std::istream& stream, Company& company) {
return stream >> company.name >> company.id;
}
std::ostream& operator<<(std::ostream& stream, const Company& company) {
return stream << company.getName() << " " << company.getId();
}
int main() {
using namespace std;
vector<Company*> companies;
string line;
while (getline(cin, line) && line != "end") {
istringstream lineIn(line);
Company* c = new Company();
lineIn >> *c;
companies.push_back(c);
}
string searchIdLine;
getline(cin, searchIdLine);
int searchId = stoi(searchIdLine);
Company* companyWithSearchedId = find(companies, searchId);
if (companyWithSearchedId != nullptr) {
cout << *companyWithSearchedId << endl;
}
else {
cout << "[not found]" << endl;
}
for (auto companyPtr : companies) {
delete companyPtr;
}
return 0;
}
And here is my incomplete attempt for completion of the Find.h file (the program should output the id and the name of the company that matches the given id):
#ifndef FIND_H
#define FIND_H
#include "Company.h"
#include <vector>
using namespace std;
Company* find(vector<Company*> vc, int id) {
for (int i = 0; i < vc.size(); i++) {
if (vc[i]->getId() == id) {
//I do not know what to write here as to return a pointer
//to the required element so as to fulfil the requirement?
}
}
return nullptr;
}
#endif // !FIND_H
One alternative is to define a functor or function object and use the std::find algorithm:
struct Find_By_ID
{
int id_to_find;
bool operator==(const Company& a)
{
return a->getId() == id_to_find;
}
}
//...
std::vector<Company> database; // Note, not a vector of pointers
//...
Find_By_ID functor;
functor.id_to_find = searchId;
std::vector<Company>::iterator iter = std::find(database.begin(), database.end(),
functor);
Edit 1: No new necessary
You don't need to use new when building your database:
Company c;
std::vector<Company> database;
while (std::cin >> c)
{
database.push_back(c);
}
The std::vector::push_back() method will make a copy and append it to the vector. The vector will allocate memory for the item as necessary.
Edit 2: Brute force
You could use a custom brute force method instead of a functor:
const std::vector<Company>::const_iterator iter_begin(database.begin());
const std::vector<Company>::const_iterator iter_end(database.end());
std::vector<Company>::const_iterator iter;
for (iter = iter_begin; iter != iter_end; ++iter)
{
if (iter->getId() == searchId)
{
break;
}
}
if (iter != iter_end)
{
std::cout << "Customer found by ID.\n";
}
If you want to modify the found Customer, change the iterator types to:
std::vector<Customer>::iterator
as appropriate.
For the specific issue in the .h file for loop try:
return vc[i]; //vc is a vector of Company pointers, this returns the pointer at vc index i
For the output part consider:
cout << companyWithSearchedId->getId() << " " << companyWithSearchId->getName() << endl;
There are more issues here at as a whole, take your time and work through it.
I have given it a few tries based on what I've read on numerous forums and books I have here. More details about this assignment here with more code from it: Click- another question from StackOverflow
What exactly I need to do is create a file with CHotel objects and insert them in this vector m_hoteli.
As to why it isn't working, it is either not reading the string from the file and it's not filling the vector at all.
This is my file:
"Marina" 5 500
"Tulip" 4 400
"BlackSea" 3 300
"SwissBell" 5 600
class CComplex:CHotel
{
protected:
string m_complex;
vector<CHotel> m_hoteli;
public:
CComplex(){};
CComplex(string filename, string nComplex)
{
fstream file("filename.txt", ios::in);
CHotel temp(" ",0,0);
while (file >> temp)
{
m_hoteli.push_back(temp);
}
/* Second try:
m_complex = nComplex;
fstream in;
in.open(filename, ios::in);
string s;
while (getline(in, s))
{
CHotel h1(s);
m_hoteli.push_back(h1);
}
Third try:
m_complex = nComplex;
ifstream iStream(filename);
if (iStream.good())
{
copy(istream_iterator<CHotel>(iStream), istream_iterator<CHotel>(), back_inserter(m_hoteli));
}
}
*/
}
That's the CHotel code:
class CHotel : public CTurist
{
protected:
string hName;
int stars;
int beds;
map<CTurist, unsigned> Turisti;
public:
unsigned Sum = 0;
int br = 0;
CHotel(){};
CHotel(string hName2, int zvezdi, int legla)
{
hName = hName;
stars = zvezdi;
beds = legla;
}
friend istream& operator>>(std::istream& is, CHotel& e)
{
is >> e.hName >> e.stars >> e.beds;;
return is;
}
I just do this in the main: CComplex c1("filename.txt", "Complex1");
You are not using the filename parameter in the CComplex constructor, other than that your code should work.
fstream file(filename, ios::in);
Do you know how to step debug? Here is some info on debugging
Here is your full code, the only change is the filename parameter and the file should now be placed in c:\temp.
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <fstream>
using namespace std;
class CTurist
{
protected:
string tName;
int age;
public:
CTurist() {};
CTurist(string name, int age2)
{
tName = name;
age = age2;
}
bool operator<(const CTurist& e) const
{
return age < e.age;
}
friend ostream& operator<<(ostream& os, const CTurist&& e);
friend ifstream& operator>>(ifstream& is, CTurist&& e);
};
class CHotel : public CTurist
{
protected:
string hName;
int stars;
int beds;
map<CTurist, unsigned> Turisti;
public:
unsigned Sum = 0;
int br = 0;
CHotel() {};
CHotel(string hName2, int zvezdi, int legla)
{
hName = hName;
stars = zvezdi;
beds = legla;
}
friend istream& operator>>(std::istream& is, CHotel& e)
{
is >> e.hName >> e.stars >> e.beds;;
return is;
}
};
class CComplex :CHotel
{
protected:
string m_complex;
vector<CHotel> m_hoteli;
public:
CComplex() {};
CComplex(string filename, string nComplex)
{
fstream file(filename, ios::in);
CHotel temp(" ", 0, 0);
while (file >> temp)
{
m_hoteli.push_back(temp);
}
}
};
int main()
{
CComplex c1("C:\\temp\\file.txt", "Complex1");
system("pause");
return 0;
}
Try putting a breakpoint in main and step through your program with f11 and f10
in my school assignment i need a small help
this is my header file:
#include <iostream>
#include <cstring>
using namespace std;
#include "ISBNPrefix.h"
class ISBN
{
char str[11];
char area[6];
char publisher[8];
char title[7];
bool registered;
public:
ISBN();
ISBN(const char*,ISBNPrefix &);
void toStr(char*)const;
void toStrWithStyle(char*)const;
bool empty()const;
bool isRegistered() const;
bool read(istream& is, const ISBNPrefix& list);
void display(ostream&) const;
};
int isValid(const char* str);
and this is the implementation of my file:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <iomanip>
using namespace std;
#include "ISBN.h"
ISBN::ISBN()
{
str[0]='\0';
area[0]='\0';
publisher[0]='\0';
title[0]='\0';
registered=false;
}
ISBN::ISBN(const char* s,ISBNPrefix& p)
{
if(isValid(s)==1)
{
strcpy_s(str,s);
}
else
{
*this=ISBN();
}
}
bool ISBN::empty()const
{
bool chk=false;
if(str[0]=='\0')
chk=true;
return chk;
}
void ISBN::toStrWithStyle(char* s) const
{
if(registered)
{
sprintf(s,"%s-%s-%s-%c",area,publisher,title,str[9]);
}
else
{
toStr(s);
}
}
void ISBN::toStr(char* s) const
{
if (str[0]!='\0')
strcpy(s,str);
else
strcpy(s,"no data");
}
void ISBN::display(ostream & os) const
{
char str[14];
toStrWithStyle(str);
cout<< setw (13) <<str;
}
int isValid(const char* str)
{
int rc=0;
if(str!=0)
{
int sum,i=0;
sum=0;
for(i=0;i<10;i++)
sum+=(str[i]-'0')*(10-i);
if(sum%11==0)
{
rc= 1;
}
}
else
rc=0;
return rc;
}
bool ISBN::read(istream& is, const ISBNPrefix& list)
{
char str[11];
bool quit=false;
bool ok=false;
char lists;
do{
cout<<"ISBN (0 to quit) : ";
is.getline(str,11); //or is.get(str,11)
if(strcmp(str,"0")==0)
quit=true;
else if (isValid(str)==1)
{
*this=ISBN(str,list);
ok=true;
cout<<"isbn is valid"<<endl;
}
else
{
*this=ISBN();
cout<<"invalid ISBN"<<endl;
}
} while(!quit&&!ok);
return !quit;
}
in the ISBN::read where I say
*this=ISBN(str,list);
i want to overload another member but i can't.
can anyone tell me how can i do that?
First I would suggest use std::string in favour of char[]. It will save a lot of trouble. For reading ISBN I would write something like this:
bool ISBN::read(istream& is)
{
ISBN result;
// reading into result
std::swap(*this,result);
return !quit;
}
Or even better (as a non member function):
std::istream& operator>>(istream& is, ISBN& obj)
{
ISBN result;
// reading into result
is(!quit)
is.clear(std::ios_base::failbit);
std::swap(obj,result);
return is;
}
In any way you should RAII classes for your resources. In your special case std::string instead of char[].
Consider the following:
I have a class defined :
class Human
{
public:
std::string Name;
std::string Age;
std::string Weight;
}
I have a .txt file defined:
Justin,22,170
Jack,99,210
Fred,12,95
etc...
The goal is to turn this text file into a std::vector
My current code is as follows:
vector<Human> vh;
std::ifstream fin(path);
std::string line;
while(std::getline(fin,line))
{
std::stringstream linestream(line);
std::string value;
Human h;
int IntSwitch = 0;
while(getline(linestream,value,','))
{
++IntSwitch;
try{
switch(IntSwitch)
{
case 1 :
h.Name = value;
break;
case 2:
h.Age = value;
break;
case 3:
h.Weight = value;
vh.push_back(h);
break;
}
}
catch(std::exception ex)
{
std::cout << ex.what() << std::endl;
}
}
}
Now i'm just curious is there any c++11 technique or non c++11 technique that would be more efficient / easier to read then this?
I write a skeleton version, it should work with line base structure:
struct Human
{
Human(const std::string& name, const std::string& age, const std::string& weight)
: name_(name), age_(age), weight_(weight) { }
std::string name_;
std::string age_;
std::string weight_;
};
class CSVParser
{
public:
CSVParser(const std::string& file_name, std::vector<Human>& human) : file_name_(file_name)
{
std::ifstream fs(file_name.c_str());
std::string line;
while(std::getline(fs, line))
{
human.push_back(ConstructHuman(line));
}
}
Human ConstructHuman(const std::string& line);
private:
std::string file_name_;
};
Human CSVParser::ConstructHuman(const std::string& line)
{
std::vector<std::string> words;
std::string word;
std::stringstream ss(line);
while(std::getline(ss, word, ','))
{
words.push_back(word);
}
return Human(words[0], words[1], words[2]);
}
int main()
{
std::vector<Human> human;
CSVParser cp("./word.txt", human);
return 0;
}