Array based on strings, char list conversions - c++

I create a multiple table of string type. I keep variables inside (int, string). It gives me an error:
[Error] cannot convert 'std::string {aka std::basic_string}' to 'char' in assignment
I've created a tree-shaped suite of functions.The program create a multiple array from a file with this format:
11 10 2001
CSKA Moscow
12 1
Bayern Munich
...
Program:
void llegir(std::fstream &_contingut, std::string *_taula) {
//declaro variables
int dia, mes, any, puntsLocal, puntsVisitant, i = 0;
std::string equipLocal, equipVisitant;
while (!(_contingut.eof())) {
//llegeixo arxiu
_contingut >> dia >> mes >> any; //primera linea
_contingut.ignore();
getline(_contingut, equipLocal); //segona linea
_contingut >> puntsLocal >> puntsVisitant; //tercera linea
_contingut.ignore();
getline(_contingut, equipVisitant); //quarta linea
_taula[i][0] = dia;
_taula[i][1] = mes;
_taula[i][2] = any;
_taula[i][3] = equipLocal.c_str();
_taula[i][4] = puntsLocal;
_taula[i][5] = equipVisitant.c_str();
_taula[i][6] = puntsVisitant;
i++;
}
}
void creartaulaDelFitxer(std::string _fitxer, std::string *_taula, int &n_taula) {
std::fstream arxiu;
arxiu.open(_fitxer, std::fstream:: in );
if (arxiu.is_open()) {
std::cout << "existeix";
} else {
std::cout << "ERROR OBERTURA FITXER";
}
llegir(arxiu, _taula);
}
int main(int argc, char** argv) {
std::string fitxer;
std::string eurolliga[300][7];
int n_taula = 0;
std::cout << "INTRODUEIX NOM FITXER:" << std::endl;
std::cin >> fitxer;
creartaulaDelFitxer(fitxer, *eurolliga, int n_taula);
}

You are mixing pointers, chars and strings which will certainly cause a lot of headache. Try to use the standard containers, like std::string and std::vector. If you need many strings, put them in a vector. When you have a collection of data like
11 10 2001
CSKA Moscow
12 1
Bayern Munich
that describes some entity, create a class for it. You can then add streaming operators for that class to read in one of these entities. If you have a collection of entities, make a container and add streaming operators for that too.
Example:
#include <iostream>
#include <fstream>
#include <vector>
class Game {
std::string equipLocal{};
std::string equipVisitant{};
int dia{}, mes{}, any{};
int puntsLocal{}, puntsVisitant{};
public:
friend std::istream& operator>>(std::istream&, Game&);
friend std::ostream& operator<<(std::ostream&, const Game&);
};
// read one entity from an istream
std::istream& operator>>(std::istream& is, Game& g) {
if(is >> g.dia >> g.mes >> g.any) {
is.ignore();
if(std::getline(is, g.equipLocal) && (is >> g.puntsLocal >> g.puntsVisitant)) {
is.ignore();
std::getline(is, g.equipVisitant);
}
}
return is;
}
// write one entity to an ostream
std::ostream& operator<<(std::ostream& os, const Game& g) {
return os << g.dia << " " << g.mes << " " << g.any << "\n"
<< g.equipLocal << "\n"
<< g.puntsLocal << " " << g.puntsVisitant << "\n"
<< g.equipVisitant << "\n";
}
class EuroLiga {
std::vector<Game> games{};
public:
bool Load(const std::string& filename) {
std::ifstream arxiu(filename);
if(arxiu) {
games.clear();
arxiu >> *this; // use this class' friend, operator>>
return true;
} else
return false;
}
// support for basic non-const iteration over the 'games'
std::vector<Game>::iterator begin() { return games.begin(); }
std::vector<Game>::iterator end() { return games.end(); }
friend std::istream& operator>>(std::istream&, EuroLiga&);
};
// read all entities from an istream
std::istream& operator>>(std::istream& is, EuroLiga& el) {
Game tmp;
while(is >> tmp) {
el.games.push_back(std::move(tmp));
}
return is;
}
int main() {
EuroLiga euroliga;
std::string fitxer;
std::cout << "INTRODUEIX NOM FITXER: ";
std::cin >> fitxer;
euroliga.Load(fitxer);
// display all entities read from the file
for(auto& g : euroliga) {
std::cout << g << "\n";
}
}

void llegir(std::fstream &_contingut, std::string *_taula)
Gets a pointer to a string called _taula, this is probably your array.
However you assign something to your array like this:
_taula[i][0] = dia; // allowed, but bad because char is basically a number.
[...]
_taula[i][3] = equipLocal.c_str(); // not allowed, you are assigning a char pointer to a char.
taula[i] is the i-th string in your array. And by putting [0] you assign to the first character in that string. dia is an integer though.
For example
std::string[] = {"Hello", "world", "I", "am", "alive"};
std::cout << string[1] << std::endl; // output "world"
std::cout << string[1][0] << std::endl; // output 'w'
You can not assign a string to a single character.
As a side note, you should look into declaring an enumeration for your array index (and a constant for it's size) to make it more clear and improve maintainability.
What you should probably be doing is create a struct or class for your, whatever it is
struct whateverItIs {
int dia, mes, any, puntsLocal, puntsVisitant;
std::string equipLocal, equipVisitant;
};
Make a new instance of that in your llegir and push it to the back of a std::vector you get by reference.
Just remember to delete() them later especially before that vector goes out of scope.

Related

How to use pointer with struct to refer to the field of each struct

Below code is the normal way to get the input from a text and store it in an array in a structure.
Wanted to ask how can i use pointer to store all these data into the array of structure ? Like p1->Years (this is without array, but how can i apply this to way of writing in below code)
Any better suggestion to use pointer to take in the input?
int years = 4;
struct maju_company {
int Year;
float quarter1, quarter2, quarter3, quarter4, total_sales, average_sales;
};
int main() {
string line;
maju_company p1[years];
fstream yeecinnfile("MajuSales.txt");
if(yeecinnfile.is_open()) {
//ignoring the first four line of code and store the rest of the code
string line1,line2,line3,line4;
getline(yeecinnfile,line1);
getline(yeecinnfile,line2);
getline(yeecinnfile,line3);
getline(yeecinnfile,line4);
while(!yeecinnfile.eof()) {
for(int i =0; i<years; i++) {
yeecinnfile>>p1[i].Year>>p1[i].quarter1>>p1[i].quarter2>>p1[i].quarter3>>p1[i].quarter4;
}
}
for(int i =0; i<years; i++) {
cout<<p1[i].Year<<setw(10)<<p1[i].quarter1<<setw(10)<<p1[i].quarter2<<setw(10)<<p1[i].quarter3<<setw(10)<<p1[i].quarter4<<endl;
}
cout<<endl;
}
}
I see nothing wrong with the way you do this.
However, you could create a pointer to each record inside the loop
maju_company* p = &p1[i];
and then use p-> instead of p1[i]., but I really don't see this as an improvement.
If the reading loop looks too complicated, I would rather move the code to a separate function, perhaps
void read_record(maju_company& company);
or maybe
maju_company read_record();
and then only have to handle a single company inside the function (so no indexing and no ponters there).
I think you wouldn't need pointers at all for your example.
Use a std::vector to hold all your data and then there are other
things from C++ I think you should learn to use, example here :
(if you have questions let me know)
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
// dont use : using namespace std;
struct maju_company_data
{
int year;
float quarter1, quarter2, quarter3, quarter4, total_sales, average_sales;
};
// describe how to stream data to an output stream (like std::cout)
std::ostream& operator<<(std::ostream& os, const maju_company_data& data)
{
os << "-----------------------------------------------------\n";
os << "Company data for year : " << data.year << "\n";
os << "Quarter 1 : " << data.quarter1 << "\n";
os << "Quarter 2 : " << data.quarter1 << "\n";
os << "Quarter 3 : " << data.quarter1 << "\n";
os << "Quarter 4 : " << data.quarter1 << "\n";
os << "\n";
return os;
}
int main()
{
// no need to manage pointers yourself use a vector
std::vector<maju_company_data> company_yearly_data; // give variables a meaningful name
std::ifstream ifile("MajuSales.txt"); // ifstream your using file as input
std::string line1, line2, line3, line4;
// ignore first line
ifile >> line1;
while (ifile >> line1 >> line2 >> line3 >> line4) // probably you need to read a few more lines here
{
maju_company_data data;
// convert read strings to numbers
data.year = std::stoi(line1);
data.quarter1 = std::stof(line2);
data.quarter2 = std::stof(line3);
data.quarter3 = std::stof(line4);
//..
//data.quarter4 = std::stof(line5);
//data.total_sales = std::stof(line6);
company_yearly_data.push_back(data);
};
// this is a range based for loop
// it is prefered since you cant go out of bounds
// const auto& means that data will be an unmodifiable
// reference to each of the structs stored in the vector
for (const auto& data : company_yearly_data)
{
std::cout << data; // since we overloaded << this loop will be nice and clean
}
return 0;
}
A C++ approach to this to overload the istream operator>> and ostream operator<< for your specific type. E.g.
#include <algorithm>
#include <array>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <string>
static constexpr auto years{4};
struct maju_company {
int Year{};
float quarter1{}, quarter2{}, quarter3{}, quarter4{};
float total_sales{}, average_sales{}; // ALWAYS init your floats.
};
auto& operator>>(std::istream& is, maju_company& mc) {
is >> mc.Year
>> mc.quarter1 >> mc.quarter2 >> mc.quarter3 >> mc.quarter4
>> mc.total_sales >> mc.average_sales;
return is;
}
auto& operator<<(std::ostream& os, maju_company const& mc) {
os << mc.Year
<< std::setw(10) << mc.quarter1
<< std::setw(10) << mc.quarter2
<< std::setw(10) << mc.quarter3
<< std::setw(10) << mc.quarter4;
return os;
}
You can then go on to use the type using the std library, e.g.
int main() {
auto p1{std::array<maju_company, years>{}};
{
auto fs{std::fstream("MajuSales.txt")};
if (!fs.is_open()) return -1;
{
// throw away 4 lines
auto dummy{std::string{}};
for (auto i{0}; i < 4; ++i) getline(fs, dummy);
}
std::copy_n(std::istream_iterator<maju_company>{fs},
years,
begin(p1));
}
std::copy(cbegin(p1), cend(p1),
std::ostream_iterator<maju_company>{std::cout, "\n"});
}

creating a class vector that does not delete it's content

I am a beginner , so i wanted to ask , can we create a class object vector/array , that does not delete it's content when i close the program like , so like I want a customer record , but whenever if we try to restart the program we need to enter the customer details again and again ...
how to prevent that from happening
#include <iostream>
#include <vector>
using namespace std;
class customer{
public:
int balance;
string name;
int password;
};
int main(){
vector <customer> cus;
...
if(choice == 1){
cout << cus[i].balance
}
return 0;
}
As a complement to Adam's answer, it is possible to encapsulate the serialization in the container class itself. Here is an simplified example:
The header file defining a persistent_vector class that saves its content to a file:
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <initializer_list>
namespace {
// Utility functions able to store one element of a trivially copyable type
template <class T>
std::ostream& store1(std::ostream& out, const T& val) {
out.write(reinterpret_cast<const char*>(&val), sizeof(val));
return out;
}
template <class T>
std::istream& load1(std::istream& in, T& val) {
in.read(reinterpret_cast<char*>(&val), sizeof(val));
return in;
}
// Specialization for the std::string type
template <>
std::ostream& store1<std::string>(std::ostream& out, const std::string& val) {
store1<size_t>(out, val.size());
if (out) out.write(val.data(), val.size());
return out;
}
template <>
std::istream& load1<std::string>(std::istream& in, std::string& val) {
size_t len;
load1<size_t>(in, len);
if (in) {
char* data = new char[len];
in.read(data, len);
if (in) val.assign(data, len);
delete[] data;
}
return in;
}
}
template <class T>
class persistent_vector {
const std::string path;
std::vector<T> vec;
// load the vector from a file
void load() {
std::ifstream in(path);
if (in) {
for (;;) {
T elt;
load1(in, elt);
if (!in) break;
vec.push_back(elt);
}
if (!in.eof()) {
throw std::istream::failure("Read error");
}
in.close();
}
}
// store the vector to a file
void store() {
std::ofstream out(path);
size_t n = 0;
if (out) {
for (const T& elt : vec) {
store1(out, elt);
if (!out) break;
++n;
}
}
if (!out) {
std::cerr << "Write error after " << n << " elements on " << vec.size() << '\n';
}
}
public:
// a bunch of constructors, first ones load data from the file
persistent_vector(const std::string& path) : path(path) {
load();
}
persistent_vector(const std::string& path, size_t sz) :
path(path), vec(sz) {
load();
};
// last 2 constructors ignore the file because they do receive data
persistent_vector(const std::string& path, size_t sz, const T& val) :
path(path), vec(sz, val) {
};
persistent_vector(const std::string& path, std::initializer_list<T> ini) :
path(path), vec(ini) {
}
// destructor strores the data to the file before actually destroying it
~persistent_vector() {
store();
}
// direct access to the vector (const and non const versions)
std::vector<T>& data() {
return vec;
}
const std::vector<T>& data() const {
return vec;
}
};
It can, out of the box, handle any trivially copyable type and std::string. User has to provide specializations of store1 and load1 for custom types.
Here is a trivial program using it:
#include <iostream>
#include <string>
#include "persistent_vector.h"
int main() {
std::cout << "Create new vector (0) or read an existing one (1): ";
int cr;
std::cin >> cr;
if (!std::cin || (cr != 0 && cr != 1)) {
std::cout << "Incorrect input\n";
return 1;
}
if (cr == 0) {
persistent_vector<std::string> v("foo.data", 0, "");
// skip to the end of line...
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
for (;;) {
std::string line;
std::cout << "Enter a string to add to the vector (empty string to end program)\n";
std::getline(std::cin, line);
if (line.empty()) break;
v.data().push_back(line);
}
}
else {
persistent_vector<std::string> v("foo.data");
for (const std::string& i : v.data()) {
std::cout << i << '\n';
}
}
return 0;
}
When a programmer creates a vector class, he must ensure that the resources acquired for that vector are released when they are no longer needed. (See RAII)
C++ Reference : https://en.cppreference.com/w/cpp/language/raii
Wikipedia : https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
Stack Overflow : What is meant by Resource Acquisition is Initialization (RAII)?
Microsoft : https://learn.microsoft.com/en-us/cpp/cpp/object-lifetime-and-resource-management-modern-cpp?view=msvc-170
Before the program closes, all resources must be released.
(No leaking resources, memory included)
It is not possible to create a vector class that does not delete its contents after closing a program. Secure operating systems will release program resources when the program is closed.
If you want the program not to lose customer information after closing, you need to save the information in persistent (non-volatile) storage device, such as a disk.
As CinCout, 김선달, Serge Ballesta say, you have to save the customer information to a file, and write the program so that you can read that file during the start of the program.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
struct customer {
std::string name;
int balance;
int password;
};
int main() {
std::vector <customer> customers;
std::ifstream ifs("info.txt");
{
customer customer{};
while (ifs >> customer.name >> customer.balance >> customer.password)
customers.push_back(customer);
}
for (const auto& [name, balance, password] : customers) {
std::cout <<
"\nName : " << name <<
"\nBalance : " << balance <<
"\nPassword : " << password <<
'\n';
}
std::cout << "\n\nWelcome\n\n";
std::ofstream ofs("info.txt", std::ios_base::app);
char cont{};
do {
customer customer{};
std::cout << "Name : ";
std::cin >> customer.name;
std::cout << "Balance : ";
std::cin >> customer.balance;
std::cout << "Password : ";
std::cin >> customer.password;
ofs << customer.name << ' ' << customer.balance << ' ' << customer.password << '\n';
std::cout << "Add another customer? (Y/N) : ";
std::cin >> cont;
} while (cont == 'Y');
for (const auto& [name, balance, password] : customers) {
std::cout <<
"\nName : " << name <<
"\nBalance : " << balance <<
"\nPassword : " << password <<
'\n';
}
}
CPlusPlus : https://www.cplusplus.com/doc/tutorial/files/
LearnCpp : https://www.learncpp.com/cpp-tutorial/basic-file-io/
(About File I/O)
This program is a prototype, I left some things incomplete (like check readings, user-defined I/O operators, duplicate code, formatting, reallocations of customers, ifs is not required after range-for + structured binding,...).
I suggest you read the book "Programming: Principles and Practice Using C+", I’m reading it and it helped me a lot.
(I’m also a beginner)
Edit: I also suggest you use "using namespace std;" only for small projects, examples or simple exercises.
Do not use "using namespace std;" for real projects, large projects or projects that may include other dependencies because the use of "using namespace std;" could lead to a possible naming collisions between names within std and the names of other codes and libraries.
It’s not good practice to use it all the time.

Using streambuf when sending/getting struct

I'm working on boost::asio::streambuf and found out that I can send/get a struct using it, but when I send a struct I just can't get it as I have sent it. The documentation says that one should use commit() and consume(), but where should I use them here?
struct person
{
int m_id;
std::string m_message;
};
std::istream& operator>>(std::istream& in, struct person& p)
{
return in >> p.m_id >> p.m_message;
}
std::ostream& operator<<(std::ostream& out, struct person& p)
{
return out << p.m_id << " " << p.m_message;
}
int main()
{
boost::asio::streambuf buf;
std::ostream out(&buf);
person p;
p.m_id = 1;
p.m_message = "Hello World!";
out << p;
std::istream in(&buf);
person p1;
in >> p1;
cout << "ID: " << p1.m_id << endl;
cout << "Message: " << p1.m_message << endl;
return 0;
}
The problem is with strings so when I type only "hello" (without world), it works fine, but if I add "world!" as shown above it just doesn't see the added "world!", why?
There are a number of issues.
Firstly, make the arguments const& when possible:
std::ostream &operator<<(std::ostream &out, person const &p) {
Secondly, make sure the streams flush to the buffer. I think it's good practice to limit the lifetime of the ostream or istream instances
Thirdly, choose a format that will be robust. Your sample already had bigger problems, when you had m_id = 1 and m_message = "123" (can you see it?).
In text formats you need either fixed-length fields or a delimiting protocol. Let's fix it:
std::ostream &operator<<(std::ostream &out, person const &p) {
return out << p.m_id << ";" << p.m_message.length() << ";" << p.m_message;
}
Now when reading it back you will see how much more precise you need to be:
std::istream &operator>>(std::istream &in, person &p) {
char separator;
size_t length;
bool ok = in >> p.m_id
&& in >> separator && separator == ';'
&& in >> length
&& in >> separator && separator == ';'
;
if (ok) {
p.m_message.resize(length);
in.read(&p.m_message[0], length);
p.m_message.resize(in.gcount());
}
// ensure the expected number of bytes were read
ok = ok && (p.m_message.length() == length);
if (!ok)
in.setstate(std::ios::failbit);
return in;
}
Yikes. Really? Yes really. At a minimum!
Do error handling
Full Demo
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
struct person {
int m_id;
std::string m_message;
};
std::ostream &operator<<(std::ostream &out, person const &p) {
return out << p.m_id << ";" << p.m_message.length() << ";" << p.m_message;
}
std::istream &operator>>(std::istream &in, person &p) {
char separator;
size_t length;
bool ok = in >> p.m_id
&& in >> separator && separator == ';'
&& in >> length
&& in >> separator && separator == ';'
;
if (ok) {
p.m_message.resize(length);
in.read(&p.m_message[0], length);
p.m_message.resize(in.gcount());
}
// ensure the expected number of bytes were read
ok = ok && (p.m_message.length() == length);
if (!ok)
in.setstate(std::ios::failbit);
return in;
}
int main() {
boost::asio::streambuf buf;
std::ostream(&buf) << person{ 1, "Hello World!" };
person received;
if (std::istream(&buf) >> received) {
std::cout << "ID: " << received.m_id << std::endl;
std::cout << "Message: " << received.m_message << std::endl;
} else {
std::cout << "Couldn't receive person\n";
}
}
Prints
ID: 1
Message: Hello World!
BONUS
C++14 added std::quoted:
#include <iomanip>
std::ostream &operator<<(std::ostream &out, person const &p) { return out << p.m_id << std::quoted(p.m_message); }
std::istream &operator>>(std::istream &in, person &p) { return in >> p.m_id >> std::quoted(p.m_message); }
Which, in this case, also does the job: Live On Coliru
From http://en.cppreference.com/w/cpp/string/basic_string/operator_ltltgtgt (emphasis and ellipsis mine):
2) ... reads characters from is and appends them to str ... until one
of the following conditions becomes true:
...
...
std::isspace(c,is.getloc()) is true for the next character c in is (this whitespace character remains in the input stream). ...
Basicaly what this means is that if you extract a string from an istream using operator >> it stops at white spaces.
If you want to get everything from the stream into your string there are plenty of questions asking that (Like this one or this one).

Using push_back for vector in c++

I am having trouble using push_back for vectors in c++.
My vector is named data.
In my loop I want to add 50 to data[i].getQuantity then push_back to data
These are things that I have tried.
data.push_back(data[i].getQuantity());
and
float myFloat = data[i].getQuantity() + 50;
data.push_back(data[i].getQuantity(myFloat));
data.push_back(myFloat);
The error is saying
No function to call to push_back
Here is my code:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
struct Input
{
friend std::istream& operator >>(std::istream& inp, Input& item);
friend std::ostream& operator <<(std::ostream& outp, Input const& item);
std::string group;
std::string total_pay;
float quantity;
// default constructor. sets up zero-elements
Input() : group(), total_pay(), quantity()
{
}
Input(std::string groupIn, std::string total_payIn, float quantityIn) :
group(std::move(groupIn)),
total_pay(total_payIn),
quantity(quantityIn)
{
}
// Accessors
std::string const& getGroup() const { return group; }
std::string getTotalPay() const { return total_pay; }
float getQuantity() const { return quantity; }
};
// global free function for extracting an Input item from an input stream
std::istream& operator >>(std::istream& inp, Input& item)
{
return (inp >> item.group >> item.total_pay >> item.quantity);
}
// global operator for inserting to a stream
std::ostream& operator <<(std::ostream& outp, Input const& item)
{
outp
<< item.getGroup() << ", "
<< item.getTotalPay() << ", "
<< item.getQuantity();
return outp;
}
struct ctype : std::ctype<char>
{
static mask* make_table()
{
static std::vector<mask> table(classic_table(),
classic_table() + table_size);
table[','] |= space;
return &table[0];
}
ctype() : std::ctype<char>(make_table()) { }
};
int main() {
std::fstream infile("employee.dat");
std::vector<Input> data;
std::string line;
try {
while (std::getline(infile, line))
{
std::istringstream iss(line);
Input inp;
iss.imbue(std::locale(iss.getloc(), new ctype));
while (iss >> inp) // calls our extraction operator >>
data.push_back(inp);
if (iss.fail() && !iss.eof())
std::cerr << "Invalid input line: " << line << '\n';
}
// dump all of them to stdout. calls our insertion operator <<
std::copy(data.begin(), data.end(),
std::ostream_iterator<Input>(std::cout,"\n"));
std::ofstream outp("output.dat");
for(int i = 0; i < data[i].getQuantity(); i++)
{
float myFloat = data[i].getQuantity() + 50;
data.push_back(myFloat);
outp << data[i].getGroup() << ',' << data[i].getTotalPay() << ',' << data[i].getQuantity() + 50 << '\n';
}
} catch (std::exception& e) {
std::cout << "There was an error: " << '\n';
return 1;
}
return 0;
}
Your vector is of type std::vector<Input>. That means you can only put objects of type Input into it. You can't push_back a float into such a vector.
If your intention is to create a new Input object and push that back into your vector, you could do something like
data.push_back(Input(data[i].getGroup(), data[i].getTotalPay(), data[i].getQuantity() + 50))
On the other hand, if you are simply trying to modify an element in data without adding a new element to data, you could just do
data[i].quantity += 50;
This works because you use a struct rather than a class. In a struct, variables default privacy level is public. If you wanted to use a class, or you just don't want to directly access the struct members, you would have to create a setter function for quantity.

How to parse content of a file and load into the map

i want to parse the content of a file and load into a map.
This is the file content format:
Movie-name release-year price cast ishd
"DDLG" 2010 20.00 "shahrukh and Kajal" true
"Aamir" 2008 10.00 "abc, xyz and ijkl" false
Key for map will be the first word (Movie name).
class defition:
class movieInfo
{
private:
int releaseYear;
double price;
string cast;
bool isHD;
};
This is the function i am trying to implement.
void fill_map_contents (map <string, movieInfo*> &mymap, ifstream& myfile)
{
string line;
string word;
while (getline(myfile, line))
{
out << "inside while loop " << line << endl;
stringstream tokenizer;
tokenizer << line;
movieInfo *temp = new movieInfo;
while (tokenizer >> word)
{
cout << " printing word :->"<< word << endl;
//temp->releaseYear = atoi(word);
//temp->price = 12.34;
//temp->cast = "sahahrukh salman";
//temp->isHD = false;
mymap[word] = temp;
}
delete temp;
}
}
I am not getting any idea, after while (tokenizer >> word), how to fill object variable and assign it to map.
Any help will be highly appreciated.
Devesh
cout << " printing word :->"<< word << endl;
//temp->releaseYear = atoi(word);
//temp->price = 12.34;
//temp->cast = "sahahrukh salman";
//temp->isHD = false;
in above code you are trying to access private members of the class directly which is not possible.Hence ,better solution is you should include public getter/setter for each variable as follows.
public:
void setreleaseYear(int sry){releaseYear=sry;}
void setprice(double pr){price=pr;}
void setcast(string cast){string=str;}
void setisHD(bool id){isHD=id;}
now use in place of commented code :
//temp->releaseYear = atoi(word);
temp->setreleaseYear(atoi(word));
tokenizer >> word;
//temp->price = 12.34;
temp->setprice(atof(word));
tokenizer >> word;
//temp->cast = "sahahrukh salman";
temp->setcast(word);
tokenizer >> word;
//temp->isHD = false;
temp->setisHD(word);
No need of while loop.
You must simplify things. I suggest to add inserction and extraction operator for movieinfo and choose new line as field separator
DDLG
2010
20.00
shahrukh and Kajal
true
Aamir
2008
10.00
abc, xyz and ijkl
false
class movieInfo
{
public:
int releaseYear;
double price;
string cast;
bool isHD;
friend std::ostream& operator << ( std::ostream& os, const movieinfo& i )
{
return os << i.releaseYear << '\n'
<< i.price << '\n'
<< i.cast << '\n'
<< std::boolalpha << i.isHD() << '\n';
}
friend std::istream& operator >> ( std::istream& is, movieinfo& i )
{
is >> i.releaseYear
>> i.price;
getline( is, i.cast );
return is >> std::boolalpha >> i.isHD;
}
};
void fill_map_contents (map <string, movieInfo> &mymap, ifstream& myfile)
{
while ( !myfile.eof )
{
string name;
myfile >> name;
movieInfo mi;
myfile >> m1;
mymap[ name ] = movieInfo;
}
}
note that I changed map <string, movieInfo*> in map <string, movieInfo> prefer using move semantic.
Another suggestion I will change moveinfo in:
class movieInfo
{
public:
// ctor and move, assign operator and move operator
int releaseYear() const { return mReleaseYear; };
double price() const { return mPrice; };
const string& cast() const { return mCast; };
bool isHD() const { return mIsHD; };
private:
int mReleaseYear;
double mPrice;
string mCast;
bool mIsHD;
friend std::ostream& operator << ( std::ostream& os, const movieinfo& i )
{
return os << i.releaseYear() << '\n'
<< i.price() << '\n'
<< i.cast() << '\n'
<< std::boolalpha << i.isHD() << '\n';
}
friend std::istream& operator >> ( std::istream& is, movieinfo& i )
{
is >> i.mReleaseYear
>> i.mPrice;
getline( is, i.mCast );
return is >> i.mIsHD;
}
};
You are effectively trying to parse a CSV file, with space as separator and " as quote character.
I'd recommend using a library for this, like this one.
Example code (taken from the help page):
// Note: I changed mymap to map<string, movieInfo> without a pointer - it's not
// needed
const char field_terminator = ' '; // Use a space
const char line_terminator = '\n'; // Use line break
const char enclosure_char = '"'; // Use "
csv_parser file_parser;
file_parser.set_skip_lines(1);
file_parser.init(filename);
file_parser.set_enclosed_char(enclosure_char, ENCLOSURE_OPTIONAL);
file_parser.set_field_term_char(field_terminator);
file_parser.set_line_term_char(line_terminator);
while(file_parser.has_more_rows()) {
csv_row row = file_parser.get_row();
movieInfo temp; // No need for pointers
temp->releaseYear = atoi(row[1]); // C++11: Use std::stoi()
temp->price = atof(row[2]); // C++11: Use std::stof()
temp->cast = row[3];
temp->isHD = row[4].compare("true") == 0;
mymap[row[0]] = temp;
}