1

In this code I have an overloaded constructor Record::Record(string s)that reads in a string, I am trying to create a string stream from 's' and use getline(stringStream, line, ",") to read each element from the string with "," as the delimiter, then assign each element to the appropriate object variable. The end goal of this assignment is to open a file, read in its data, assign the data appropriately in a vector, then write and parse the data to a new file.

My understanding of working with private class members is limited. I am unsure how to go about writing the constructor. In the past I've used a pointer for private members (e.g 'this-> foo;), at this point I just need to understand how to implement the contents of Record, so far what I've tried has been incorrect and I can only find references to using pointers to int's.

Normally I would go to my comp-sci lab and ask a TA but it is currently close due to COVID.

Here is the code for my constuctors and overloaded operators.

Record.cpp

    #include <string>
    #include <sstream>
    #include "Record.h"

    using namespace std;

    Record::Record(string s) {

  /**  here is where I need to assign data to the following.
                std::string department;
                std::string item_code;
                int quantity;
                double cost;  **/
    }

    Record::~Record() {
        // TODO Auto-generated destructor stub
    }

    //overloaded "==" and "<" comparison operators

    bool operator ==(const Record &lhs, const Record &rhs){
        return (lhs.cost == rhs.cost && lhs.department == rhs.department &&
                lhs.item_code == rhs.item_code && lhs.quantity == rhs.quantity);
    }

    /**bool operator <(const Record &a, const Record &b){ //do not need at this time

    }
    **/

    //Overloaded "<<" operator
    std::ostream& operator <<(std::ostream& os, const Record& r){
        os << r.department << ',' << r.item_code << ',' <<  r.quantity << ',' << r.cost;
        return os;

    }

Record.h

#ifndef RECORD_H_
#define RECORD_H_

#include <iostream>
#include <string>

class Record {
public:

    //Constructor
    Record(std::string s); //pass this string to our Record class

    //De-constructor
    virtual ~Record();

    //overloaded "==" and "<" comparison operators

    friend bool operator ==(const Record &a, const Record &b);

    //friend bool operator <(const Record &a, const Record &b);  //Do not need at this time.

    //Overloaded "<<" operator

    friend std::ostream& operator <<(std::ostream&, const Record&);


private:
        std::string department;
        std::string item_code;
        int quantity;
        double cost;

};

#endif /* RECORD_H_ */

Main.cpp

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <libgen.h>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>
#include "Record.h"
using namespace std;

int main() {

    vector<Record> records; //vector of type Records to hold each "Line" of input file
    string filename;        // File name and path stored as string

    /**
     * Prompts user for the name of input file and stores in string variable filename
     *
     */
    cout << "please enter the name of your file with file path:" << endl;
    cin >> filename;
    ifstream ifs { filename.c_str() };
    if (!ifs) {
        cerr << " Can't open file " << filename << endl;
        return 1;
    }

    string path = filename.substr(0, filename.find_last_of("\\/"));
    string file = filename.substr(filename.find_last_of("\\/") + 1,
            filename.length());
    if (path.compare(file) == 0) {
        path == "";
    }
    //test for file and file path

    cout << "Path portion of " << filename << " is " << path << endl;
    cout << "File portion of " << filename << " is " << file << endl; // path + "new_" + file + ".cvs", make new file with new path

    /**
     * Put each line of input file in to the records vector
     */

    string line; //strings for each parameter of the vector object


    while (getline(ifs, line)) {

        Record newRecord(line); //this should check for duplicates and ignore any found.
        int i = 0;
        int n = 0;
            if((newRecord == records[i]) || (i < n) ){
                i++;
            }
            else{
                records.push_back(newRecord);
                n++;
                i = 0;
            }
    }
    ifs.close(); //closes the stream

    //create new file and output data to it
    string newFile = ("new_" + file + ".cvs");

    //check to see if file path and file name are correct
    cout << (path + newFile);


    //Open stream to new file and write to it

    ofstream ofs(path + newFile);
    ofs.open(newFile);

    for(size_t i = 0; i < records.size(); i++){
        ofs << (i+1) << ' ' << records[i];
    }

    ofs.close(); //close output stream

    return 0;
}
Ruiji
  • 21
  • 3
  • Normally I would use the [Member Initializer List](https://en.cppreference.com/w/cpp/language/initializer_list), but here it would result in unnecessarily complicated code or unnecessary duplication of effort. I'm just dropping the link here for your future use. You do not need to use the `this->member` notation unless you reuse the member's name and ["Shadow"](https://en.wikipedia.org/wiki/Variable_shadowing) the member. – user4581301 Apr 03 '20 at 22:27
  • Your stated plan is sound. You should attempt it and then ask questions about the attempt if necessary. – user4581301 Apr 03 '20 at 22:29

1 Answers1

1

You can do something like:

Record::Record(std::string s){

    std::string word;
    std::vector<std::string> temp;
    std::stringstream ss(s);

    while(getline(ss, word, ','))
        temp.push_back(word);

    department = temp.at(0);
    item_code =  temp.at(1);
    quantity = stoi(temp.at(2));
    cost = stod(temp.at(3));
}

I'm assuming you mean that each parmeter is separated by ',', not each line, if that's not the case, say something.

Edit

So there are a couple of issues in your main function, namely, the while getline cycle will probably have out_of_range access, you can use a range-based for loop, which avoids container overflow:

while (getline(ifs, line))
{
    bool flag = 1;
    Record newRecord(line); 

    for(Record r : records){
        if(r == newRecord){
            flag = 0;
            break;
        }
    }
    if(flag)
        records.push_back(newRecord);
}

The ofstream file is not being properly opened and tested, you can do something like:

ofstream ofs;
ofs.open(path + newFile);

if (!ofs)
{
    cerr << "Can't open file " << filename << endl;
    return 1;
}

for (size_t i = 0; i < records.size(); i++)
{
    ofs << (i + 1) << ' ' << records[i] << endl;
}

This line:

path == "";

I'm sure you meant:

path = "";

Running sample

One last note, using namespace std is not a very good practice.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • Thanks, I've make the constructor like you suggested. Now my issue is when I run the code it terminates with exit value -1. I'm guessing it's not outputting the data correctly. – Ruiji Apr 04 '20 at 00:25
  • 1
    @Ruiji, there were some issues in your main function, I edited in some corrections in the answer with a running sample. – anastaciu Apr 04 '20 at 11:49