create objects based on entries from a file - c++

How to create objects during runtime based on input from file.
I have few entries in a file in an organized way separated by a space
entryname type satus
entry1 type1 yes
entry2 type2 no
...
I would like to create objects of below class based on number of entries.
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
class Entry{
std::string name;
std::string type;
std::string availability;
};
void getEntriesInfo(std::vector<std::string> &info)
{
std::string line;
std::fstream myfile("entries.txt",std::ios::in);
if (myfile.is_open())
{
//remove first line from file.
getline(myfile,line);
while ( getline(myfile,line) )
info.push_back(line);
myfile.close();
}
else std::cout << "Unable to open file";
}
int main()
{
std::vector<std::string> entries_info;
getEntriesInfo(entries_info);
for(auto &e: entries_info)
std::cout<<e<<std::endl;
return 0;
}
EDIT 1:
I have restructured code a bit using maps and vectors, can it be made more elegant.
If you look at the code , I have not used constructors & destructors properly, (I could not figure out a good way to use), also the setters and not written all kinds of constructors(deep copy or move) etc..
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
//#include <istream>
#include <map>
class AItem;
AItem makeItem(std::string );
void displayItemInfo(std::vector<std::string> info);
class Item {
public:
virtual ~Item() = default;
virtual std::string name() const = 0 ;
virtual std::string quota() const = 0 ;
virtual std::string make() const = 0 ;
};
class AItem : Item{
private:
std:: string _name;
std:: string _quota;
std:: string _make;
public:
virtual std::string name() const { return _name;}
virtual std::string quota() const { return _quota;}
virtual std::string make() const {return _make;}
void setName();
void setQuota();
void setMake();
AItem(){}
AItem(std::string name,std::string quota,std::string make) :_name(name),_quota(quota),_make(make) {}
friend std::istream& operator>>(std::istream& in,AItem& item);
friend std::ostream & operator << (std::ostream &out, const AItem & item);
};
void displayItemInfo(std::vector<std::string> info)
{
for(auto &i_it:info)
std::cout<<i_it<<std::endl;
}
std::vector<std::string> readItemInfo(const char *str)
{
std::string line;
std::vector<std::string> itemInfo;
std::ifstream file(str, std::ios::in);
if(file.is_open()){
getline(file,line);
while(getline(file,line))
itemInfo.push_back(line);
file.close();
}else{
std::cout<<"Unable to open"<<str<<std::endl;
}
return itemInfo;
}
std::ostream &operator << (std::ostream &out,const AItem& item)
{
//std::cout <<item.name()<<" "<<item.quota()<<" "<<item.make() <<std::endl;
return out <<item.name()<<" "<<item.quota()<<" "<<item.make() <<std::endl;
}
std::istream& operator >> (std::istream& in,AItem& item) {
//in >> item._name >> item._quota >> item._make;
return in >> item._name >> item._quota >> item._make;
}
AItem makeItem(std::string aitem)
{
std::stringstream ss(aitem);
std::vector<std::string> arguments;
std::string entry;
while (ss >> entry) {
arguments.push_back(entry);
}
AItem aItem_obj(arguments.at(0),arguments.at(1),arguments.at(2));
return aItem_obj;
}
std::map<std::string,AItem> createItems(std::vector<std::string> item)
{
std::map<std::string,AItem> ItemMap;
for(auto &i_it:item) {
AItem a_item = makeItem(i_it);
ItemMap.insert(std::make_pair(a_item.name(),a_item));
}
// for(auto &m:ItemMap)
// std::cout<<m.first<<m.second<<std::endl;
return ItemMap;
}
int main(int argc, char*argv[])
{
char *str = "player_info.txt";
std::vector<std::string> info;
std::map<std::string,AItem> ItemMap;
// info = readItemInfo(str);
// displayItemInfo(info);
//#if 0
ItemMap = createItems(readItemInfo(str));
for(auto &item:ItemMap)
std::cout<<item.first<<item.second<<std::endl;
//#endif
return 0;
}
EDIT 2
I have done this way
entry.h
#include <iostream>
#include <string>
class Entry{
private:
std::string name;
std::string type;
std::string availability;
public:
std::string getName(void) const;
std::string getType(void) const;
std::string getAvailability(void) const;
void setName(const std::string n);
void setType(const std::string t);
void setAvailability(const std::string y);
Entry();
~Entry();
friend std::ostream &operator << (std::ostream &out,const Entry &e);
};
entry.cpp
#include "entry.h"
std::string Entry ::getName(void) const{
return name;
}
std::string Entry ::getType(void) const{
return type;
}
std::string Entry ::getAvailability(void) const{
return availability;
}
void Entry ::setName(const std::string n){
name = n;
}
void Entry ::setType(const std::string t){
type = t;
}
void Entry ::setAvailability(const std::string y){
availability = y;
}
Entry::Entry(){
}
Entry::~Entry(){
}
std::ostream &operator << (std::ostream &out,const Entry &e){
return out << e.getName()<<" " <<e.getType()<<" "<<e.getAvailability()<<std::endl;
}
main.cpp
#include <fstream>
#include <vector>
#include "entry.h"
void makeEntry(std::string line,std::vector<Entry> &entries)
{
Entry P1;
P1.setName(line.substr(0, line.find(' ')));
P1.setType(line.substr(P1.getName().size(), line.find(' ')));
P1.setAvailability(line.substr(P1.getName().size()+P1.getType().size(), line.find(' ')));
//std::cout<<"C:"<<P1.getName()<<P1.getType()<<P1.getAvailability()<<std::endl;
entries.push_back(P1);
}
void getEntriesInfo(std::vector<std::string> &info)
{
std::string line;
std::fstream myfile("entries.txt",std::ios::in);
if (myfile.is_open())
{
//remove first line from file.
getline(myfile,line);
while ( getline(myfile,line) )
info.push_back(line);
myfile.close();
}
else std::cout << "Unable to open file";
}
int main()
{
std::vector<std::string> entries_info;
std::vector<Entry> entries;
getEntriesInfo(entries_info);
for(auto &e: entries_info) {
std::cout<<e<<std::endl;
makeEntry(e,entries);
}
std::cout<<"OUT"<<std::endl;
for(auto &e: entries) {
std::cout<<e<<std::endl;
}
return 0;
}

You can provide an overload for the stream extraction operator:
std::istream& operator>>(std::istream& in,Entry& e) {
in >> e.name >> e.type >> e.availability;
}
This works, when the individual entries in the file do not contain spaces. Ie it would fail for
entryName LastName type1 yes
In this case you would need to have a seperator (eg ;) and use std::getline to parse the lines.
Reading entries into a vector is then (assuming you know the number of entries beforehand):
std::vector<Entry> readFromFile(std::istream& myfile,size_t n_entries) {
std::vector<Entry> entries;
data.resize( n_entries );
for (auto& e : entries ) myfile >> e;
return entries;
}

Related

std::list doesn't initialize correctly after it's content gets read from fstream

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

Completing the body of a function in a separate header file in C++?

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.

Reading a file into a vector of class objects

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

overloading a member inside another member

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[].

What is the best way to create a vector of meaningful data out of a comma delimited text file

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