Попытка Ввести в член структуры внутри вектора структур вызывает ошибку сегментации - PullRequest
0 голосов
/ 09 ноября 2018

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

Этот блок кода - все, что связано с проблемой.

`

#include <iostream>
#include <fstream>
#include <vector>
//there is more code to this program, but the fault occurs very soon in the program
//and none of the rest of the code has any relevance.
//also, I don't really think that the problem is with trying to input, but I don't have enough experience to rule it out.
using namespace std;

struct menuItemType
{
  string menuItem; //this is the name of the item
  double menuPrice; // this is the price of the item
  int menuCount;
};

vector<menuItemType> menuList; //the menu can be any size so I don't know how big it will be at this point. I'm using a vector to avoid having to declare a size
// I also have 2 other functions and some extra code in main that all need to access this vector. That is why I made it global

void getData() //this function opens the text file containing the menu and tries to read in each line.
{
    ifstream input;
    input.open("inData.txt");

    input.peek();
    int i = 0;
    string item;
    double price;

    while(!input.eof())
    {
        getline(input,menuList[i].menuItem); //This is the line creating the fault.
        input >> menuList[i].menuPrice;

        i++;
        input.peek();
    }
}
int main()
{
    getData();
    return 0;
}

`

Я попытался отладить и определил, что ошибка сегментации не относится к строке, прокомментированной во фрагменте кода. Кажется, что ошибка возникает всякий раз, когда я пытаюсь ввести член структуры внутри вектора. Я также попытался использовать cin, поэтому не верю, что поток текстовых файлов является проблемой. Текстовый файл выглядит так:

Bacon and eggs 1.00 Muffin 0.50 Coffee 0.90

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

Извините за длинное объяснение и неудобное форматирование. Я довольно плохо знаком с переполнением стека и с ++.

Ответы [ 3 ]

0 голосов
/ 09 ноября 2018

Сначала удалите «$» из inData.txt, затем я предлагаю использовать while (getline (input, item)) следующим образом:

#include <iostream>
#include <fstream>
#include <vector>
#include <math.h>
//there is more code to this program, but the fault occurs very soon in the program
//and none of the rest of the code has any relevance.
//also, I don't really think that the problem is with trying to input, but I don't have enough experience to rule it out.
using namespace std;

struct menuItemType
{
  string menuItem; //this is the name of the item
  double menuPrice; // this is the price of the item
  int menuCount;
};

vector<menuItemType*> menuList; //the menu can be any size so I don't know how big it will be at this point. I'm using a vector to avoid having to declare a size
// I also have 2 other functions and some extra code in main that all need to access this vector. That is why I made it global

void getData() //this function opens the text file containing the menu and tries to read in each line.
{
    ifstream input;
    input.open("inData.txt");

    int i = 0;
    string item;
    double price;

    while(getline(input, item))
    {
        menuList.push_back(new menuItemType);
        menuList[i]->menuItem = item;
        getline(input,item);
        menuList[i]->menuPrice = atof((char*)item.c_str()); //math.h
        i++;
    }
}
int main()
{
    getData();
    for(menu : menuList)
    {
        cout << menu->menuItem << ": " << menu->menuPrice << endl;
        delete menu; //cleaning memory
    }
    return 0;
}
0 голосов
/ 09 ноября 2018

при извлечении данных из файла; Я предпочитаю извлекать содержимое одной строки и сохранять его в какой-либо строке, потоке или буфере и анализировать его позже, или я получу все содержимое файла и сделаю то же самое. Мне легче разобрать строку после того, как вы извлекли данные из файла и закрыли его дескриптор. Я не люблю использовать глобальные переменные, которые НЕ являются CONST. Также то, как вы используете цикл for при чтении из файла while( file.eof() ) или while ( !file.eof() ), является плохой практикой и может привести к множеству ошибок, сбоев и головных болей в дальнейшем. Если вы посмотрите на мою функцию ниже, все, что она делает, это берет имя файла и пытается открыть его, если оно существует. После того, как он откроется, он получит строку, сохранит ее в строку и вставит эту строку в вектор, пока больше не будет ничего для чтения. Затем он закрывает дескриптор файла и возвращает. Это соответствует концепции функции с единственной ответственностью.

Если у вас есть функция, в которой вы открываете файл, читаете строку, анализируете данные, читаете строку, анализируете данные и т. Д., Затем закрываете их; Считается, что такого рода функции выполняют несколько задач, что может быть плохо. Сначала есть причины производительности. Открытие и чтение из самого файла является, так сказать, дорогой вычислительной задачей. Вы также пытаетесь создавать объекты на лету, и это может быть плохо, если вы никогда не проверяли достоверность значений, полученных из файла. Посмотрите на мой код ниже, и вы увидите шаблон проектирования, на который я ссылаюсь, где каждая функция несет свою ответственность. Это также помогает предотвратить file corruption.

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

struct MenuItem {
  string menuItem; 
  double menuPrice; 
  int menuCount;
};

// This function is not used in this case but is a very helpful function
// for splitting a string into a vector of strings based on a common delimiter
// This is handy when parsing CSV files {Comma Separated Values}.
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();
}

void parseFileData( const std::vector<std::string>& fileContents, std::vector<MenuItem> menuItems ) {
    // The first param is the contents of the file where each line
    // from the file is stored as a string and pushed into a vector.

    // Here you need to parse this data. The second parameter is the
    // vector of menu items that is being passed by reference.

    // You can not modify the fileContents directly as it is const and read only
    // however the menuItems is passed by reference only so you can update that

    // This is where you will need to go through some kind of loop and get string
    // of text that will stored in your MenuItem::menuItem variable.
    // then the next string will have your price. Here you showed that your
    // text file has `$` in front of the value. You will then have to strip this out 
    // leaving you with just the value itself. 
    // Then you can use `std::stod( stringValue ) to convert to value, 
    // then you can save that to MenuTiem::menuPrice variable.

    // After you have the values you need then you can push back this temp MenuItem
    // Into the vector of MenuItems that was passed in. This is one iteration of
    // your loop. You continue this until you are done traversing through the fileContents vector.


    // This function I'll leave for you to try and write.        
}

int main() {
    try {
        std::vector<std::string> fileConents;
        getDataFromFile( "test.txt", fileConents );
        std::vector<MenuItem> data; // here is the menu list from your example
        generateVectors( fileConents, data );

        // test to see if info is correct
        for( auto& d : data ) {
            std::cout << data.menuItem << " " << data.menuPrice << '\n';
        }

    } catch( const std::runtime_error& e ) {
        std::cerr << e.what() << '\n';
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

Что касается вашей ошибки или сбоя, вы, вероятно, либо обращались к индексу, который находится за концом вектора, либо пытались использовать содержимое вектора, в котором были неверные данные.

0 голосов
/ 09 ноября 2018

Если вы посмотрите на оператор [] вектора , а затем отметите раздел исключений, он скажет вам, что если n превосходит размер вектора, это фактически неопределенное поведение. Возможно, вы хотите отодвинуть назад созданный вами элемент.

Я обычно предпочитаю vector :: at , так как он проверяется на наличие границ и сигнализирует, находится ли запрошенная позиция вне допустимого диапазона, генерируя исключение out_of_range.

...