Почему я получаю эту ошибку, нарушающую мою программу? С++

У меня есть программа для моего класса С++, которая по какой-то причине продолжает ломаться, когда я дохожу до определенного момента в моей программе.

Вот заголовочный файл моего

// 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

И это мой файл cpp, содержащий мой основной:

#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";
        }

    }
}

Задача состояла в том, чтобы создать программу, которая выполняет все действия, описанные в меню помощи. Я начал с «i», который предназначен для ввода информации текстовых файлов в массив объектов InventoryItem, содержащих описание предмета, стоимость предмета и общее количество единиц предмета. Кроме того, должен быть включен номер элемента инвентаря, но место в массиве, удерживаемое объектом, совпадает с номером элемента, поэтому переменная не требуется.

Пробую запустить программу, работает. Я набираю «i» в качестве своей команды. Просит файл на вход, я использую файл "plumbing.txt". Plumbing.txt содержит следующий текст, разделенный вертикальной чертой:

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

Как только я нажму Enter после ввода «plumbing.txt», программа запустится.

Это выглядит так:

"команда: i

Введите имя файла для ввода: plumbing.txt" И тогда программа прерывается.

Я сузил часть кода до того, что на самом деле вызывает сбой программы, и что-то в этой части кода:

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);
            }

Пожалуйста, помогите мне понять, почему моя программа продолжает ломаться. Я не могу разобраться. Я пытаюсь прочитать ввод в виде строк из файла, разобрать строки на отдельные фрагменты данных. Преобразуйте строку в целое или двойное число, используя stoi или stof, и введите информацию в массив объектов. Спасибо за чтение и любую помощь, которую вы можете предложить.


person Hunter Thompson    schedule 20.08.2016    source источник
comment
Итак, как это ломается, точно? Какую именно ошибку вы получаете? Вы прошли через код в отладчике? Какие значения вы ожидали от используемых переменных? Что ты на самом деле нашел?   -  person code_dredd    schedule 20.08.2016
comment
for(int j = jTracker; num < variable; j++) Когда 0 будет меньше 0?   -  person Retired Ninja    schedule 20.08.2016


Ответы (1)


В вашем коде есть две проблемы, связанные с использованием циклов for, когда лучше использовать while. Первая проблема связана с блоком кода:

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
}

Здесь count увеличивается даже после того, как мы достигли конца файла. В результате count будет на единицу больше, чем количество строк в файле. Вместо этого мы можем использовать цикл while:

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

Здесь getline установит флаг конца файла для inFile ifstream, когда будет прочитана последняя строка, так что цикл while выйдет. С count, инициализированным нулем перед этим циклом while, результирующее count будет правильно отражать количество строк в файле.

Вторая проблема связана с блоком кода:

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;
}

Как указывали другие, num и variable оба устанавливаются равными нулю перед этим циклом for, так что условие num < variable никогда не выполняется. Поэтому код внутри цикла for никогда не выполняется. Здесь снова подойдет цикл while:

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;

Здесь find вернет std::string::npos, если больше нет разделителей (т.е. "|") для поиска. Когда j инициализируется нулем перед внешним циклом k for, этот цикл while будет анализировать все токены, разделенные символом "|", кроме последнего. Поэтому после цикла while мы извлекаем последнюю подстроку, используя std::string::npos, чтобы обозначить, что нам нужны все символы от oldDelimiter до конца строки. Обратите внимание, что переменные newLine, num, variable и jTracker не нужны.

На самом деле, мы можем объединить два цикла while, чтобы получить:

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;
}

что устраняет необходимость во внешнем цикле k for. Также обратите внимание, что массив parsingArray больше не нужен, так как больше нет необходимости хранить промежуточные результаты, накопленные в одном цикле, для повторения и использования в следующем цикле.

Дополнительные примечания для рассмотрения:

  1. Используйте std::vector вместо массивов фиксированного размера для std::string и InventoryItem.
  2. Объявляйте переменные «локально» по причинам, обсуждаемым в этой ссылке< /а>.
  3. В последующем цикле for, который устанавливает items, предполагается, что файл содержит ровно четыре токена для каждой строки (и что токены — это именно номер товара (целое число), описание (символьная строка), стоимость (действительное число) , и единица измерения (целое число) именно в таком порядке). Вы должны тщательно продумать, что произойдет, если файл не удовлетворяет этому требованию (т. е. ошибка входных данных), и как ваша программа должна обрабатывать все случаи ошибок. Может быть, код в этом цикле можно включить во вложенные циклы while, чтобы можно было обнаруживать и правильно обрабатывать ошибки при анализе файла?
person aichao    schedule 20.08.2016