-1

I have a program for my c++ class that for some reason keeps breaking when I get to a certain point in my program.

Here is the header file of my

// This class has overloaded constructors.
#ifndef INVENTORYITEM_H
#define INVENTORYITEM_H
#include <string>
using namespace std;

class InventoryItem
{
private:
   string description; // The item description
   double cost;        // The item cost
   int units;          // Number of units on hand
   int inventoryItemNumber; //Used to sort items from first entered to last
public:
   // Constructor #1
   InventoryItem()
      { // Initialize description, cost, and units.
        description = "";
        cost = 0.0;
        units = 0; }

   // Constructor #2
   InventoryItem(string desc)
      { // Assign the value to description.
        description = desc;

        // Initialize cost and units.
        cost = 0.0;
        units = 0; }

   // Constructor #3
   InventoryItem(string desc, double c, int u)
      { // Assign values to description, cost, and units.
        description = desc;
        cost = c;
        units = u; }

   // Mutator functions
   void setDescription(string d) 
      { description = d; }

   void setCost(double c)
      { cost = c; }

   void setUnits(int u)
      { units = u; }

   // Accessor functions
   string getDescription() const
      { return description; }

   double getCost() const
      { return cost; }

   int getUnits() const
      { return units; }
};
#endif

And this is my cpp file containing my main:

#include "InventoryItem.h"
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;

int main(){

    InventoryItem item[1000];
    string parsingArray[1000];
    char command;
    ifstream inFile;
    string inFileName;
    //The typecasting variables from string to int,string,double,int

    //other possible integers ROUND 2
    int itemNumber;
    string description;
    double cost;
    int units;

    //possible variables:
    int count = 0;
    int jTracker = 0;
    string parsingArray2[1000];

    while(true){
        cout << "Command: ";
        cin  >> command; cin.ignore(80, '\n');

        if (command == 'a') {
            //Add parts: increase the units value for an existing inventory item.
        }
        else if (command == 'h') {
            //Prints Help Text
            cout << "Supported commands: \n"
                << "                 a    Add parts.\n"
                << "                 h    print Help text.\n"
                << "                 i    Input inventory data from a file.\n"
                << "                 p    Print invetory list.\n"
                << "                 n    New invetory Item.\n"
                << "                 o    Output invetory data to a file.\n"
                << "                 q    quit (end the program).\n"
                << "                 r    Remove Parts.\n"
                << endl;
        }
        else if (command == 'i') {
            //Input inventory data from a file.
            do{
                cout << "Enter name of input file: ";
                getline(cin, inFileName);
                inFile.open(inFileName);
                if (inFile.fail())
                {

                    cout << "Failed to open file: " << inFileName << "\n\n";

                }
            }while(inFile.fail());

            //write each line to string
            for (int i = 0; inFile; i++) {
                getline(inFile, parsingArray[i], '\n');
                count++;//count will be needed for counting iterations of for loop for InventoryItem object data field completion
            }

            for (int k = 0; k < count; k++)
            {
                int newLine = 0;
                int num = 0;
                int oldDelimiter = 0, newDelimiter = 0;
                int variable = 0;

                for (int j = jTracker; num < variable; j++) {//jTracker continues to grow through multiple outer "k" loops.
                    newDelimiter = parsingArray[k].find("|", oldDelimiter);      //newDelimiter becomes the further pipe delimiter in the line.
                    parsingArray2[j] = parsingArray[k].substr(oldDelimiter, ((newDelimiter)-oldDelimiter));//the pipe delimited strings are isolated in input2[]
                    oldDelimiter = newDelimiter + 1;//oldDelimiter is set to the next pipe delimiter.


                    variable = parsingArray[k].length();
                    //The following alters variables as needed for the next loop
                    num = newDelimiter;
                    jTracker = (j + 1);
                    newLine = j;
                }
            }


            for(int y = 0; y < count; y++)
            {
                int itemNumber = stoi(parsingArray2[0+(4*y)]);
                string description = parsingArray2[1+(4*y)];
                double costs = stof(parsingArray2[2+(4*y)]);
                int unit = stoi(parsingArray2[3+(4*y)]);
                item[itemNumber].setDescription(description);
                item[itemNumber].setCost(costs);
                item[itemNumber].setUnits(unit);
            }

            cout << count << " records loaded to array.\n";

        }
        else if (command == 'p') {

        }
        else if (command == 'j') {

        }
        else if (command == 'o') {

        }   
        else if (command == 'q') {
            // Quit.
            cout << "Exit." << endl;
            system("pause");
            return 0;
        }
        else if (command == 'r') {

        }
        else {
            // Invalid user input, re-prompted.
            cout << "Invalid command.\n";
        }

    }
}

The assignment was to create a program that does all the actions described in the help menu. I started with "i" which is to input text files' information into an array of InventoryItem onjects, containing description of item, cost of item and total number of item units. Also, inventoryitem # should be included, but the array spot held by the object is the same as the item number, so a variable isn't needed.

I try running the program, it runs. I type in "i" as my command. It asks for a file to input, I use the file "plumbing.txt". Plumbing.txt contains the following pipe-delimited text:

0|Pump|39.00|20
1|Gasket|1.50|29
2|Water Level Gauge|12.99|30
3|Faucet Repair Kit|4.89|8
4|Teflon Thread Seal Tape (50 ft roll)|3.30|12
5|shutoff valve|6.50|10

Once i hit enter after typing in "plumbing.txt", the program programs.

It looks like this:

"command: i

Enter file name for input: plumbing.txt" And then the program breaks.

I narrowed down the part of the code to what actually causes the program to break and its something in this part of the code:

for(int y = 0; y < count; y++)
            {
                int itemNumber = stoi(parsingArray2[0+(4*y)]);
                string description = parsingArray2[1+(4*y)];
                double costs = stof(parsingArray2[2+(4*y)]);
                int unit = stoi(parsingArray2[3+(4*y)]);
                item[itemNumber].setDescription(description);
                item[itemNumber].setCost(costs);
                item[itemNumber].setUnits(unit);
            }

Please help me figure out why my program keeps breaking. I cannot figure it out. I'm trying to read the input as strings from the file, parse the strings into individual pieces of data. Convert from string to integer or double using stoi or stof and enter the info into the array of objects. Thank you for reading and any help you can offer.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 1
    So, how does it "break", *exactly*? What is the exact error you're getting? Did you step through the code in the debugger? What values were you expecting in the variables being used? What did you actually find? – code_dredd Aug 20 '16 at 04:35
  • 1
    `for(int j = jTracker; num < variable; j++)` When will 0 be less than 0? – Retired Ninja Aug 20 '16 at 05:18

1 Answers1

0

There are two issues with your code involving the use of for loops when it is better to use while. The first issue involves the block of code:

for (int i = 0; inFile; i++) {
    getline(inFile, parsingArray[i], '\n');
    count++;//count will be needed for counting iterations of for loop for InventoryItem object data field completion
}

Here, count is incremented even after we have reached the end of the file. As a result, count will be one more than the number of lines in the file. We can use a while loop instead:

while (getline(inFile, parsingArray[count], '\n')) 
  ++count;

Here, getline will set the end-of-file flag for the inFile ifstream when the last line is read so that the while loop exits. With count initialized to zero before this while loop, the resulting count will correctly reflect the number of lines in the file.

The second issue involves the block of code:

for (int j = jTracker; num < variable; j++) {//jTracker continues to grow through multiple outer "k" loops.
    newDelimiter = parsingArray[k].find("|", oldDelimiter);      //newDelimiter becomes the further pipe delimiter in the line.
    parsingArray2[j] = parsingArray[k].substr(oldDelimiter, ((newDelimiter)-oldDelimiter));//the pipe delimited strings are isolated in input2[]
    oldDelimiter = newDelimiter + 1;//oldDelimiter is set to the next pipe delimiter.


    variable = parsingArray[k].length();
    //The following alters variables as needed for the next loop
    num = newDelimiter;
    jTracker = (j + 1);
    newLine = j;
}

As pointed out by others, num and variable are both set to zero before this for loop so that the condition num < variable is never met. Therefore, the code within the for loop is never executed. Again, a while loop here will do:

while ((newDelimiter = parsingArray[k].find("|", oldDelimiter)) != std::string::npos) {
    parsingArray2[j] = parsingArray[k].substr(oldDelimiter, ((newDelimiter)-oldDelimiter)); //the pipe delimited strings are isolated in input2[]
    oldDelimiter = newDelimiter + 1; //oldDelimiter is set to the next pipe delimiter.
    ++j; // increment j
}
// get the last token and increment j
parsingArray2[j] = parsingArray[k].substr(oldDelimiter, std::string::npos); //the pipe delimited strings are isolated in input2[]
++j;

Here, find will return std::string::npos if there are no more delimiters (i.e., "|") to find. With j initialized to zero before the outer k for loop, this while loop will parse all tokens delimited by "|" except for the last one. Therefore, after the while loop, we extract the last substring using std::string::npos to denote that we want all characters from oldDelimiter until the end of the string. Note that the variables newLine, num, variable, and jTracker are all not needed.

In fact, we can combine the two while loops to get:

std::string line;  // don't need parsingArray any more
int j = 0;  // initialize j before looping over file
while (std::getline(inFile, line, '\n')) {
    int oldDelimiter = 0, newDelimiter = 0;
    while ((newDelimiter = line.find("|", oldDelimiter)) != std::string::npos) {
        parsingArray2[j] = line.substr(oldDelimiter, ((newDelimiter)-oldDelimiter)); //the pipe delimited strings are isolated in input2[]
        oldDelimiter = newDelimiter + 1; //oldDelimiter is set to the next pipe delimiter.
        ++j; // increment j
    }
    // get the last token and increment j
    parsingArray2[j] = line.substr(oldDelimiter, std::string::npos); //the pipe delimited strings are isolated in input2[]
    ++j;
    // increment count
    ++count;
}

which eliminates the need for the outer k for loop. Note also that the array parsingArray is no longer needed as there is no longer a need to store intermediate results accumulated from one loop to be iterated over and used in a subsequent loop.

Additional notes to consider:

  1. Use std::vector instead of fixed size arrays for std::string and InventoryItem.
  2. Declare variables "locally" for reasons discussed in this link.
  3. In the subsequent for loop that sets the items, there is an assumption that the file contains exactly four tokens for each line (and that the tokens are exactly the item number (integer number), description (character string), cost (real number), and unit (integer number) in that order). You should carefully consider what will happen if the file does not satisfy this requirement (i.e., input data error), and how your program should handle all error cases. Maybe the code within that loop can be incorporated within the nested while loops so that errors can be detected and properly handled as the file is being parsed?
Community
  • 1
  • 1
aichao
  • 7,375
  • 3
  • 16
  • 18