C++ read line to class vector - c++

I have next c++ class called "Contact":
class Contact
{
private:
std::string contactName;
double subscriptionPrice;
int minutesIncluded;
public:
Contact(const std::string &contactName, double subscriptionPrice,
int minutesIncluded) : contactName(contactName), subscriptionPrice(subscriptionPrice), minutesIncluded(minutesIncluded)) {}
Contact() {
}
...gettetrs and setters
}
I have text file with one or more contacts in format:
asd,1.00000,1
In main method I have method that add properly vector of contacts in this text file. Problem is when I try to read from it. My target is to convert text file into vector of contacts. Method I use is next:
void phonebook_load(vector<Contact> &contacts)
{
string line;
ifstream phonebook_file;
vector<std::string> lines;
phonebook_file.open(phonebook_filename);
if(!phonebook_file.is_open())
cout << "Phonebook file could not be openned !!!" << endl;
else
{
while (phonebook_file.good())
{
for (string line; getline(phonebook_file, line, ','); )
lines.push_back(line);
}
phonebook_file.close();
}
}
I have two options:
Read line by line (which I cannot split by ",")
Split by "," which print every property of contact on new line, and I don't see how tho handle it from there.
What should I change in my method in order to read file line by line and properly convert it to vector<Contact>

Provide stream extraction and stream insertion operators for your type:
#include <string>
#include <vector>
#include <iterator>
#include <fstream>
#include <iostream>
class Contact
{
private:
std::string contactName;
double subscriptionPrice;
int minutesIncluded;
public:
Contact() {}
Contact(const std::string &contactName, double subscriptionPrice, int minutesIncluded)
: contactName { contactName },
subscriptionPrice { subscriptionPrice },
minutesIncluded { minutesIncluded }
{}
// declare the stream extraction and stream insertion operators as firends
// of your class to give them direct access to members without the need for
// getter and setter functions.
friend std::istream& operator>>(std::istream &is, Contact &contact);
friend std::ostream& operator<<(std::ostream &os, Contact const &contact);
};
std::istream& operator>>(std::istream &is, Contact &contact)
{
std::string contact_name;
if (!std::getline(is, contact_name, ',')) // use getline with a delimiter
return is; // to allow whitespace in names
// which >> doesn't
char seperator;
double subscription_price;
int minutes_included;
if (!(is >> subscription_price >> seperator >> minutes_included) || seperator != ',') {
is.setstate(std::ios::failbit);
return is;
}
contact = Contact{ contact_name, subscription_price, minutes_included };
return is;
}
std::ostream& operator<<(std::ostream &os, Contact const &contact)
{
os << contact.contactName << ", " << std::fixed << contact.subscriptionPrice
<< ", " << contact.minutesIncluded;
return os;
}
int main()
{
std::ifstream is{ "test.txt" };
std::vector<Contact> contacts{ std::istream_iterator<Contact>{ is },
std::istream_iterator<Contact>{} };
for (auto const &c : contacts)
std::cout << c << '\n';
}

Related

Writing to an array class will not cout

The code runs, but I cannot get cout to work. Please help me, I am a beginner, and really struggling with getting the contents of my array to output.
cout << myArray[0].getSquareName(); is the line that never cout's.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
class cSquare {
public:
string SquareName;
string getSquareName() const;
void setSquareName(string);
friend std::istream & operator >> (std::istream & is, cSquare & s);
};
// set method
void cSquare::setSquareName(string squareName)
{
squareName = SquareName;
}
//square name get method
string cSquare::getSquareName() const
{
return SquareName;
}
ostream & operator << (ostream & os, const cSquare & s)
{
os << s.getSquareName() << ' ';
return os;
}
istream & operator >> (istream & is, cSquare & s)
{
is >> s.SquareName;
return is;
}
int main()
{
string discard;
int i = 0;
const int MAX_SIZE = 26;
ifstream monopoly("monopoly.txt", ios::in);
if (monopoly.is_open())
{
cSquare myArray[MAX_SIZE];
getline(monopoly, discard);
string sname; //string to store what I read in from my file
while (i < MAX_SIZE && monopoly >> sname)
{
myArray[i].setSquareName(sname);//stores the string read in into the array
cout << myArray[0].getSquareName(); //it never cout's this
i++;
}
}
}
Your setSquareName() method is assigning the object's SquareName member to the input parameter, which is wrong. You need to do the opposite instead, eg:
void cSquare::setSquareName(string sname)
{
//sname = SquareName;
SquareName = sname;
}
Also, this line:
cout << myArray[0].getSquareName();
Should be this instead:
cout << myArray[i];
With those 2 changes, the code works.
Demo

fread is reading garbage in file

I am working on my assignment and faced a problem with fread() in C++. When I modify the name in my file it modifies it perfectly as I want but the problem occurs when I try to read the file after that, it reads the whole file but it does not stop after that it's running total 146 times whereas there are only 3 names.
My code:-
#include <bits/stdc++.h>
using namespace std;
struct person{
int id;
string fname;
}s;
void write(){
FILE *outfile;
struct person input;
int num,ident;
string sname[] = {"a","b","c"};
outfile = fopen ("C:\\Users\\Amritesh\\Desktop\\students.txt","wb");
if (outfile == NULL)
{
fprintf(stderr, "\nError opend file\n");
exit (1);
}
scanf("%d",&num);
for(int i=0;i<num;i++){
s.fname = sname[i];
cin >> s.id;
fwrite (&s, sizeof(s), 1, outfile);
}
fclose(outfile);
}
void read(){
FILE *file1;
int i=0;
file1 = fopen ("C:\\Users\\Amritesh\\Desktop\\students.txt","r");
while(fread(&s, sizeof(s), 1, file1) == 1) {
cout << "ID " << s.id << " Name " <<s.fname << endl;
}
fclose (file1);
}
void modify(){
FILE *file;
file = fopen ("C:\\Users\\Amritesh\\Desktop\\students.txt","r+");
while(fread(&s, sizeof(s), 1, file)) {
if(s.fname == "a"){
s.fname = "d";
fseek(file,-sizeof(s),SEEK_CUR);
fwrite (&s, sizeof(s), 1,file);
}
}
fclose (file);
}
int main(){
write();
modify();
read();
}
Edited code:-
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
struct person
{
int id;
string fname;
}s,temp;
void read()
{
int num;
ifstream fin;
fin.open("C:\\Users\\Amritesh\\Desktop\\student.txt",ios::in);
fin.seekg(0,ios::beg);
//scanf("%d",&num);
while(fin){
cout << s.fname << s.id << endl;
}
fin.close();
}
void write(){
int i=0;
ofstream fout;
fout.open("C:\\Users\\Amritesh\\Desktop\\student.txt");
while(i!=2) {
cin >> s.id >> s.fname;
fout << "ID " << s.id << " Name " <<s.fname << endl;
i++;
}
fout.close();
}
void modify(){
fstream mod;
mod.open ("C:\\Users\\Amritesh\\Desktop\\student.txt");
while(mod) {
if(s.fname == "a"){
s.fname = "d";
mod.seekg(-sizeof(s),ios::cur);
mod << s.fname;
}
}
mod.close();
}
int main(){
write();
read();
modify();
}
Thanks for any answer!
Here are three ideas based on our discussion. I'll start with free functions for reading and writing a person object since it looks like you're at that stage right now. I'll move on to adding member functions in your person class and end with adding stream operators for convenience.
An example of free (non-member) read and write functions:
#include <iostream>
#include <string>
#include <fstream>
struct person {
int id;
std::string fname;
};
std::ostream& write(std::ostream& os, const person& p) {
os << p.id << ',' << p.fname << '\n'; // stream out the properties of a person
return os; // look at the next example for an alternative doing the same thing
}
std::istream& read(std::istream& is, person& p) {
// extract "id" and if it succeeds, check if the next char is a , char
if(is >> p.id && is.peek() == ',') {
is.ignore(); // step over the , char
std::getline(is, p.fname); // read the rest of the line into p.fname
} else {
// we didn't get id or the , char, so set the stream in a failed state
is.setstate(is.failbit);
}
return is;
}
int main() {
// write to file
{
std::ofstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test1{10, "Foo Bar"};
person test2{20, "Apa Bepa"};
write(file, test1);
write(file, test2);
}
// read from file
{
std::ifstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test;
while(read(file, test)) {
std::cout << test.fname << '\n';
}
}
}
An example of making read and write member functions in person:
#include <iostream>
#include <string>
#include <fstream>
struct person {
int id;
std::string fname;
std::ostream& write(std::ostream& os) const {
// this does the same thing as in the first example
return os << id << ',' << fname << '\n';
}
std::istream& read(std::istream& is) {
if(is >> id && is.peek() == ',') {
is.ignore(); // step over the , char
std::getline(is, fname);
} else {
is.setstate(is.failbit); // we didn't get id or the , char
}
return is;
}
};
int main() {
// write to file
{
std::ofstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test1{10, "Foo Bar"};
person test2{20, "Apa Bepa"};
test1.write(file);
test2.write(file);
}
// read from file
{
std::ifstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test;
while(test.read(file)) {
std::cout << test.fname << '\n';
}
}
}
Member functions supported by stream operators:
#include <iostream>
#include <string>
#include <fstream>
struct person {
int id;
std::string fname;
std::ostream& write(std::ostream& os) const {
return os << id << ',' << fname << '\n';
}
std::istream& read(std::istream& is) {
if(is >> id && is.peek() == ',') {
is.ignore(); // step over the , char
std::getline(is, fname);
} else {
is.setstate(is.failbit); // we didn't get id or the , char
}
return is;
}
};
// stream operators calling member functions
std::ostream& operator<<(std::ostream& os, const person& p) { return p.write(os); }
std::istream& operator>>(std::istream& is, person& p) { return p.read(is); }
int main() {
// write to file
{
std::ofstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test1{10, "Foo Bar"};
person test2{20, "Apa Bepa"};
file << test1 << test2; // calling operator<< for each person object
}
// read from file
{
std::ifstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test;
while(file >> test) { // extract one person at a time using operator>>
std::cout << test.fname << '\n';
}
}
}

Extracting data from a CSV file in C++

The content of my test csv file looks as follows:
*test.csv*
name;age;weight;height;test
Bla;32;1.2;4.3;True
Foo;43;2.2;5.3;False
Bar;None;3.8;2.4;True
Ufo;32;1.5;5.4;True
I load the test.csv file with the following C++ program that prints the file's content on the screen:
#include <iostream>
#include <vector>
#include <string>
#include <iomanip>
#include <fstream>
#include <sstream>
void readCSV(std::vector<std::vector<std::string> > &data, std::string filename);
void printCSV(const std::vector<std::vector<std::string>> &data);
int main(int argc, char** argv) {
std::string file_path = "./test.csv";
std::vector<std::vector<std::string> > data;
readCSV(data, file_path);
printCSV(data);
return 0;
}
void readCSV(std::vector<std::vector<std::string> > &data, std::string filename) {
char delimiter = ';';
std::string line;
std::string item;
std::ifstream file(filename);
while (std::getline(file, line)) {
std::vector<std::string> row;
std::stringstream string_stream(line);
while (std::getline(string_stream, item, delimiter)) {
row.push_back(item);
}
data.push_back(row);
}
file.close();
}
void printCSV(const std::vector<std::vector<std::string> > &data) {
for (std::vector<std::string> row: data) {
for (std::string item: row) {
std::cout << item << ' ';
}
std::cout << std::endl;
}
}
I have the following questions:
How can I load only rows where, for example, age == 32?
How can I load, for example, only the name, weight columns?
How can I exclude rows that contain None?
How can I skip the first row of the document?
Does it make more sense to extract the desired information after I loaded the entire csv file (if memory is not a problem)? If possible, I want to use only the STL.
Any assistance you can provide would be greatly appreciated
you can try some csv libraries, but if you want to do this with custom code then
Inside printCSV you ask the cin to enter column names
Maintain it in a variable
In this code for (std::vector<std::string> row: data)
Check the item again each of those input when first the loop runs
then inside the second loop keep an index, accordingly you skip the column number
Example code to print only two columns
void printCSV(const std::vector<std::vector<std::string> > &data) {
int col = 0;
std::vector<std::string> column_filter;
std::vector<int> column_index;
column_filter.push_back("name");
column_filter.push_back("weight");
int row1 =0;
for (std::vector<std::string> row: data) {
col = 0;
if(row1==0) {
int col1 = 0;
for (std::string item: row) {
for (std::string colname: column_filter){
if(item.compare(colname)==0) {
column_index.push_back(col1);
}
}
col1++;
}
}
for (std::string item: row) {
int found =0;
for (int index: column_index) {
if(index==col) found = 1;
}
if(found==1)
std::cout << item << ' ';
col++;
}
std::cout << std::endl;
row1++;
}
}
Output
name weight
Bla 1.2
Foo 2.2
Bar 3.8
Ufo 1.5
Before you close. Here all answers in one file. But I will then explain in your single questions then later.
#include <iostream>
#include <vector>
#include <string>
#include <iomanip>
#include <fstream>
#include <regex>
#include <algorithm>
#include <iterator>
// Save typing Work
using Rec = std::vector<std::string>;
std::regex delimiter{ ";" };
// Proxy class for easier input and output
struct Record {
// Our data for one record
Rec data{};
// Overwrite extractor operator
friend std::istream& operator >> (std::istream& is, Record& r) {
// Read on complete line from the input stream, and check, if the read was successfull
if (std::string line{}; std::getline(is, line)) {
// If there is something in our data vector already, delete it
r.data.clear();
// Now, in one statement, split the string into tokens and copy the result into our data vector
std::copy(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {}, std::back_inserter(r.data));
}
return is;
}
// Overwrite inserter for easier output
friend std::ostream& operator << (std::ostream& os, const Record& r) {
std::copy(r.data.begin(), r.data.end(), std::ostream_iterator<std::string>(os,"\t"));
return os;
}
};
// Proxy for the complete CSV file
struct Roster {
// The header
Rec header{};
// All records of the CSV file
std::vector<Record> records{};
friend std::istream& operator >> (std::istream& is, Roster& r) {
// Read on complete line from the input stream, and check, if the read was successfull
if (std::string line{}; std::getline(is, line)) {
// So, we just have read the header
// Now, in one statement, split the string into tokens and copy the result into the header
r.header.clear();
std::copy(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {}, std::back_inserter(r.header));
// Now, in one statement, read all lines, split the string into tokens and copy the result into our record vector
r.records.clear();
std::copy(std::istream_iterator<Record>(is), {}, std::back_inserter(r.records));
}
return is;
}
// Overwrite inserter for easier output
friend std::ostream& operator << (std::ostream& os, const Roster& r) {
std::copy(r.records.begin(), r.records.end(), std::ostream_iterator<Record>(os, "\n"));
return os;
}
};
int main() {
// Open CSV file and check, if it could be opened
if (std::ifstream csvFileStream("r:\\test.csv"); csvFileStream) {
Roster roster{};
// Read the complete CSV file
csvFileStream >> roster;
// Show all read data on std::cout
std::cout << roster;
// All records with age ==32
std::cout << "\n\nAge 32\n";
std::vector<Record> age32{};
std::copy_if(roster.records.begin(), roster.records.end(), std::back_inserter(age32), [](const Record& r) { return r.data[1] == "32"; });
for (const Record& r : age32) std::cout << r << "\n";
// Or
std::cout << "\n\nAge 32 Option 2\n";
csvFileStream.clear(); csvFileStream.seekg(std::ios::beg); age32.clear();
std::copy_if(std::istream_iterator<Record>(csvFileStream), {}, std::back_inserter(age32), [](const Record& r) { return r.data[1] == "32"; });
for (const Record& r : age32) std::cout << r << "\n";
// Get Name and weight columns
std::cout << "\n\nweight and columns\n";
std::vector<std::vector<std::string>> nameAndWeight{};
std::transform(roster.records.begin(), roster.records.end(), std::back_inserter(nameAndWeight),
[](const Record& r) { std::vector<std::string>rec{ r.data[0], r.data[2] } ; return rec; });
for (const std::vector<std::string>& r : nameAndWeight) std::cout << r[0] << "\t" << r[1] << "\n";
// Everything but none
std::cout << "\n\nEverything but none\n";
std::vector<Record> notNone{};
std::copy_if(roster.records.begin(), roster.records.end(), std::back_inserter(notNone), [](const Record& r) { return r.data[1] != "None"; });
for (const Record& r : notNone) std::cout << r << "\n";
}
else {
std::cerr << "\n*** Error: Could not open source file\n";
}
return 0;
}

Reading from a struct from a binary file and inserting in a vector/STL::list and then returning it

I am reading a structure named "Highscore" from a binary file. I print the read data in the console and it looks fine. After that, I insert them into a vector with the push_back method and when I'm done, I return the vector.
After the return line, I get the following error (translated from French):
"Exception not handled at 0x00007FF6223FF017 in Project1.exe : 0xC0000005 : access violation while reading from 0xFFFFFFFFFFFFFFFF.".
Here is my code:
struct Highscore{
string username;
int points;
};
vector<Highscore> ReadFromHighscores(){
fstream highscoresFiles;
highscoresFiles.open("highscores.db", ios::in | ios::out | ios::binary | ios::app);
highscoresFiles.seekg(0);
Highscore output;
vector<Highscore> highscores;
highscoresFiles.read(reinterpret_cast<char *>(&output), sizeof(Highscore));
cout << "Read : " << output.points << output.username << endl;
Highscore temp;
temp.points = output.points;
temp.username = output.username;
if (!highscoresFiles.eof()) {
highscores.push_back(temp);
}
highscoresFiles.close();
return highscores;
}
As having been pointed out, std::string contains a char* (which you are saving and restoring) to the real data. The actual data is not saved and the pointer you restore is causing the crash. To fix that you can create operators for streaming to/from files and dump the username c-string (including the terminating \0) and then write the points int raw. This should work as long as your usernames don't contain \0 in the middle of the string (unusual but valid).
#include <iostream>
#include <iomanip>
#include <vector>
#include <fstream>
struct Highscore{
std::string username;
int points;
Highscore(const std::string& n, int p) : username(n), points(p) {}
Highscore() : Highscore("",0) {}
// operator to save a Highscore to file
friend std::ofstream& operator<<(std::ofstream&, const Highscore&);
// operator to read a Highscore from file
friend std::ifstream& operator>>(std::ifstream&, Highscore&);
// operator to print a Highscore on other ostreams (like std::cout)
friend std::ostream& operator<<(std::ostream&, const Highscore&);
};
std::ofstream& operator<<(std::ofstream& os, const Highscore& hs) {
os.write(hs.username.c_str(), hs.username.size()+1);
os.write(reinterpret_cast<const char*>(&hs.points), sizeof(hs.points));
return os;
}
std::ifstream& operator>>(std::ifstream& is, Highscore& hs) {
std::getline(is, hs.username, '\0');
is.read(reinterpret_cast<char*>(&hs.points), sizeof(hs.points));
return is;
}
std::ostream& operator<<(std::ostream& os, const Highscore& hs) {
os << std::setw(15) << std::left << hs.username << std::setw(8) << std::right << hs.points;
return os;
}
int main() {
// create "highscores.db"
{
std::vector<Highscore> highscores {
{"MadBrother33", 234234},
{"Barny", 14234},
{"Bart", 1434}
};
std::ofstream out("highscores.db", std::ios::binary);
for(auto& hs : highscores) {
out << hs;
}
}
// read "highscores.db"
{
std::vector<Highscore> highscores;
{
Highscore hs;
std::ifstream in("highscores.db", std::ios::binary);
while(in>>hs) {
highscores.emplace_back(std::move(hs));
}
}
for(auto& hs : highscores) {
std::cout << hs << "\n";
}
}
}
Output
MadBrother33 234234
Barny 14234
Bart 1434

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.