2

I'm new to C++ and I'm running into an issue on one of my assignments. The goal is to load data from a data file that looks like this.

item number date    quantity    cost per each
1000       6/1/2018    2            2.18
1001       6/2/2018    3            4.44
1002       6/3/2018    1            15.37
1001       6/4/2018    1            4.18
1003       6/5/2018    7            25.2

Basically I need to do calculations the average item number used for each date using arrays and do some other calculations with the cost. I'm getting really hung up with loading the data from the file and manipulating it for equations. This is what I have so far.

#include <cmath> //for math operations
#include <iostream> //for cout
#include <cstdlib> //for compatibility
#include <fstream>
#include <string>
using namespace std;

int main() 
{   
string date;
int EOQ, rp;
int count;
int itemnum[][];
double quantity[][];
double cost[][];
ifstream myfile; 
string filename;
cout << "Data File: " << endl;
cin >> filename; // user enters filename

myfile.open(filename.c_str());

if(myfile.is_open())
{
    cout << "file opened" << endl;
    string head;
    while(getline(myfile, head))
    {
    break; // so header won't interfere with data
    }
while(!myfile.eof())
{ // do this until reaching the end of file

int x,y;

myfile >> itemnum[x][y] >> date >> quantity[x][y] >> cost[x][y];


cout << "The numbers are:" << endl;
for(count = 0; count < y; count++)
{
cout << itemnum[x][y] << endl; 
break;
}

//cout << "Item:         Reorder Point:       EOQ: " << endl;
//cout << itemnum << "      " << rp << "          " << EOQ << endl;

break;
}
}
else
{
    cout << "" << endl; //in case of user error
    cerr << "FILE NOT FOUND" << endl;
}


cout << endl;
cout << "---------------------------------------------" << endl;
cout << "   End of Assignment A8" << endl;
cout << "---------------------------------------------" << endl;
cout << endl;

system("pause");
return 0;

I haven't started working with the equations yet since I still can't get the file loaded in a simple array!!!

Thank you!

Link for data file : https://drive.google.com/file/d/1QtAC1bu518PEnk4rXyIXFZw3AYD6OBAv/view?usp=sharing

Nick
  • 21
  • 2
  • Could you post the file that you are trying to load? I guess I am asking if it is a text file as shown at the top of your posting, or if it is a binary file of some sort. – Gardener Oct 30 '18 at 03:12
  • 1
    `int itemnum[][];` -- This is not valid C++, and neither are any of the lines that look similar to this. – PaulMcKenzie Oct 30 '18 at 03:14
  • Here's the link - https://drive.google.com/file/d/1QtAC1bu518PEnk4rXyIXFZw3AYD6OBAv/view?usp=sharing – Nick Oct 30 '18 at 03:17
  • I left those blank purposefully - should I place a varaible in the brackets for itemnum or numbers? – Nick Oct 30 '18 at 03:19
  • @Nick *should I place a varaible in the brackets for itemnum or numbers?* -- This is the most important part that you left out. You cannot put a runtime value in the brackets -- arrays are fixed in size. Do you know the maximum number of items to read? If not, then you can't use arrays -- use `std::vector` instead. If you do know the maximum number of entries, then the maximum number of entries that will be read is placed in the brackets. You just can't leave them blank -- that is not valid C++. – PaulMcKenzie Oct 30 '18 at 03:21
  • What is your thinking behind declaring 2D variables? I only see 1D for each item in the data. Also, arrays have to be initialized with constants known at compile time in standard C++. Is it required to use standard arrays or is there a specific C++ compiler you are requested to use? – doug Oct 30 '18 at 03:22
  • @doug -- Try not to suggest using VLA's. That is not real C++. – PaulMcKenzie Oct 30 '18 at 03:23
  • The data is 30 rows long and 4 wide but since the date contains slashes I am forced to use four separate arrays with different data types (I think). – Nick Oct 30 '18 at 03:23
  • @Nick -- Then you should make the date an array of `std::string` also. Also, there is no need for a 2 dimensional array of anything. Everything is 1 dimensional. In honesty, an array of a `struct` that has 4 separate members would be much better than 4 separate arrays. – PaulMcKenzie Oct 30 '18 at 03:26
  • @PaulMcKenzie. I am not suggesting he use VLAs, didn't mention them, and pointed out they had to be initialized with constants in standard C++. I was fishing to see if his instructor might have been suggesting it. An appalling notion to teach C++ using this. I'm guessing from the question that structs haven't been discussed yet. – doug Oct 30 '18 at 03:27
  • @doug ok. It's hard to try and keep up with correcting a lot of the code that's posted from newbies and "competition coders" that believe VLA's are valid C++. – PaulMcKenzie Oct 30 '18 at 03:29
  • Thanks @PaulMcKenzie for the help. I am not familiar with struct but I'll check it out, although I may not be able to use it since we aren't allowed to use things we technically haven't learned in class. – Nick Oct 30 '18 at 03:33
  • @doug I don't think the instructor is suggesting VLA's. the data file is a set size and the code doesn't need to be able to adapt for other data files for this assignment – Nick Oct 30 '18 at 03:34
  • Does your data file have this line at the top `item number date quantity cost per each` before the actual data or contents or did you just display that so that we know what the values mean? – Francis Cugler Oct 30 '18 at 03:38
  • @FrancisCugler Yes the data files has that line at the top of the .dat file. I include the getline portion of the code to try to 'go around' it but I'm not sure that it's working – Nick Oct 30 '18 at 03:40
  • @Nick. Good! The fixed size wasn't obvious until the download link you posted. VLAs are non-standard C++ and are problematic even when allowed. Good luck. It's a great language but is highly tied to hardware (close to the metal) so you will want to pay a lot of attention to how data gets stored in memory. Use your debugger early and often. Even when things appear to work. Understand what's happening behind the scenes. Good chance the instructor is intending to introduce structs soon. This task is a teaser so you will LOVE structs. – doug Oct 30 '18 at 03:42
  • I'm still hung up on the myfile >> itemnum[30] >> date >> quantity[30] >> cost[30] part. I put it in a for loop from 0 to 30 and the program is exiting and giving a random return value – Nick Oct 30 '18 at 03:54
  • s/b 0 to 29, not 30. Are you using a debugger or just executing the code? Also, you might break the file read into smaller chunks for debugging. Like `myfile >> itemnumber[I]; myfile >> date; etc. Also, if you dimension an array as 30, you can only read in to `itemnumber[29]` and you should be reading in indexed from I=0 to 29 – doug Oct 30 '18 at 04:17
  • @Nick okay that's fine. I just wanted to know; but I worked on your problem without that single line. I'll post my answer; this is not an exact solution as I had to make some adjustments to your data file to fit my working code. However you can use this as a reference to fit your data file's structure. – Francis Cugler Oct 30 '18 at 04:21

1 Answers1

1

When working on these kinds of problems I like to break these down into the parts related to parsing. I'm using some of the standard libraries to do some of the work for me. I also created a couple of structures to help keep the information of the data organized. As for your date, I could of left that as a single std::string but I chose to break the date down into three individual types themselves and store them into a data structure just to show the capabilities of one of the functions that is involved with parsing.

What I prefer doing is to get either a single line of data from a file and save that to a string, or get the entire contents of a file and save that either to a large buffer or a vector of strings, unless if I'm handling specific type of code where that is not applicable such as parsing a wav file. Then close the file handle as I'm done reading from it! Then after I have all of the information I need, instead of trying to parse the file directly while it is opened I'd rather parse a string as it is easier to parse. Then after parsing the string we can populate our data types that we need.

I had to modify your data file slightly to accommodate for the extra white spaces so I saved your file as a text file with only a single white space between each data type within a single line of text. I also did not include the first line (header) information as I just omitted it completely. However this should still act as a guide of how to design a good work flow for an application that has good readability, reusability, try to keep it portable and as generic as possible. Now, what you have been waiting for; the demonstration of my version of your code:


#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <exception>

struct Date {
    int month;
    int day;
    int year;

    Date() = default;
    Date( int monthIn, int dayIn, int yearIn ) :
        month( monthIn ),
        day( dayIn ),
        year( yearIn ) 
    {}
};

struct DataSheetItem {
    int itemNumber;
    Date date;
    int quantity;
    double costPerEach;

    DataSheetItem() = default;
    DataSheetItem( int itemNumberIn, Date& dateIn, int quantityIn, double costPerEachIn ) :
        itemNumber( itemNumberIn ),
        date( dateIn ),
        quantity( quantityIn ),
        costPerEach( costPerEachIn ) 
    {}
};

std::vector<std::string> splitString( const std::string& s, char delimiter ) {
    std::vector<std::string> tokens;
    std::string token;
    std::istringstream tokenStream( s );
    while( std::getline( tokenStream, token, delimiter ) ) {
        tokens.push_back( token );
    }

    return tokens;
}

void getDataFromFile( const char* filename, std::vector<std::string>& output ) {
    std::ifstream file( filename );
    if( !file ) {
        std::stringstream stream;
        stream << "failed to open file " << filename << '\n';
        throw std::runtime_error( stream.str() );
    }

    std::string line;

    while( std::getline( file, line ) ) {
        if ( line.size() > 0 ) 
            output.push_back( line );
    }
    file.close();
}

DataSheetItem parseDataSheet( std::string& line ) {    
    std::vector<std::string> tokens = splitString( line, ' ' ); // First parse with delimeter of a " "

    int itemNumber = std::stoi( tokens[0] );
    std::vector<std::string> dateInfo = splitString( tokens[1], '/' );
    int month = std::stoi( dateInfo[0] );
    int day   = std::stoi( dateInfo[1] );
    int year  = std::stoi( dateInfo[2] );
    Date date( month, day, year );
    int quantity = std::stoi( tokens[2] );
    double cost = std::stod( tokens[3] );

    return DataSheetItem( itemNumber, date, quantity, cost );
}

void generateDataSheets( std::vector<std::string>& lines, std::vector<DataSheetItem>& dataSheets ) {
    for( auto& l : lines ) {
        dataSheets.push_back( parseDataSheet( l ) );
    }
}

int main() {
    try {
        std::vector<std::string> fileConents;
        getDataSheetItemsFromFile( "test.txt", fileContents );
        std::vector<DataSheetItem> data;
        generateDataSheets( fileConents, data );

        // test to see if info is correct
        for( auto& d : data ) {
            std::cout << "Item #: " << d.itemNumber << " Date: "
                << d.date.month << "/" << d.date.day << "/" << d.date.year
                << " Quantity: " << d.quantity << " Cost: " << d.costPerEach << '\n';
        }
    
    } catch( const std::runtime_error& e ) {
        std::cerr << e.what() << '\n';
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

NOTE This will not work with how your file currently is; this does not account for the first line of text (header information) and this does not account for any extra white spaces in between the data fields. If you add a single line of text when opening the file and read in a single line and just ignore it, then perform the loop to get all strings to add to vector to return back; your vectors will have the information in it but they will not be at the correct index locations of the vector because of all the extra white spaces. This is something you need to be aware of! Other than that; this is how I would basically design a program or application to parse data. This is by all means not 100% full proof and may not even be 100% bug free, but from a quick glance and running it through my debugger a few times it does appear to be without any noticeable bugs. There could also be some room for improvements for runtime efficiency, etc. but this is just a generalization of basic parsing.

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59