首页 > 解决方案 > 无法填充结构数组

问题描述

我的程序应该解析文件中的字符串并存储在结构数组中。

示例:Skyfall, 1.109, Sam Mendes, 11/9/12, 143。程序将解析字符串并存储标题、总片酬、导演姓名等。

每当我运行代码时,它似乎都没有正确存储它。

另外,我收到此错误。在抛出 'std::logic_error' what() 的实例后调用终止:basic_string::_M_construct null 无效

这是我的结构:

  struct Movie
    {
        string Title; // Movie title 
        string Gross; // Gross total in billion dollars 
        string Director; // Director name 
        string Date; // Release date 
        string Runtime; // Runtime in minutes 
    };

这个函数是创建对象数组并打开文件

Movie* createDatabase(int& number_Of_Lines)
    {
        // input file 
        ifstream movie_file;
        string filename;
        do 
        {
            cout << "Please enter filename: " ;
            getline (cin , filename);
            movie_file.open(filename.c_str());
            if(movie_file.fail())
                cout << "Invalid file" << endl ; 
        }while(movie_file.fail());

        // array of objects
        number_Of_Lines = numberOfLines(movie_file);
        Movie* ptr = new Movie [number_Of_Lines]; 

        //Looping through array of objects 
        for(int i = 0 ; i < number_Of_Lines ; i++)
            populateMovieFromFile(movie_file, ptr[i]);

        return ptr; 
    }

此函数填充对象

void populateMovieFromFile(ifstream& movie_file, Movie& movies)
{
    getline(movie_file, movies.Title, ',');
    movie_file.ignore();
    getline(movie_file, movies.Gross, ',');
    movie_file.ignore();
    getline(movie_file, movies.Director, ',');
    movie_file.ignore();
    getline(movie_file, movies.Date, ',');
    movie_file.ignore();
    getline(movie_file, movies.Runtime);
}

完整程序:

#include <iostream> 
#include <string>
#include <fstream> 
#include <iomanip> 
#include <cctype>

using namespace std; 

struct Movie
{
    string Title; // Movie title 
    string Gross; // Gross total in billion dollars 
    string Director; // Director name 
    string Date; // Release date 
    string Runtime; // Runtime in minutes 
};

int numberOfLines(ifstream&);
void populateMovieFromFile(ifstream&, Movie&);
void displayMovie(const Movie&);
Movie* createDatabase(int&);
bool caseInsensitiveCmp(string, string);
void findMovie(Movie*, int);
void saveToFile(const Movie&);
bool promptToContinue();
//void displayFavorites();

int main ()
{
    int number_Of_Lines = 0;
    Movie* ptr_movies = createDatabase(number_Of_Lines); 
    do
    {
        findMovie(ptr_movies , number_Of_Lines);
    }while (!promptToContinue());
    //displayFavorites();
    return 0;
}

int numberOfLines(ifstream& movie_file)
{
    int number_Of_Lines = 0; 
    string lines;
    if(movie_file)
    {
        while(getline(movie_file, lines))
            number_Of_Lines++ ; 
    }
    movie_file.seekg (0, ios::beg);
    return number_Of_Lines;
}

void populateMovieFromFile(ifstream& movie_file, Movie& movies)
{
    getline(movie_file, movies.Title, ',');
    movie_file.ignore();
    getline(movie_file, movies.Gross, ',');
    movie_file.ignore();
    getline(movie_file, movies.Director, ',');
    movie_file.ignore();
    getline(movie_file, movies.Date, ',');
    movie_file.ignore();
    getline(movie_file, movies.Runtime);

}

void displayMovie(const Movie& movie)
{
    cout << right << setw(13) << "Title: " << left << movie.Title << endl; 
    cout << right << setw(13) << "Gross Total: " << left << movie.Gross << " billion dollars" << endl;
    cout << right << setw(13) << "Director: " << left << movie.Director << endl;
    cout << right << setw(13) << "Release date: " << left << movie.Date << endl; 
    cout << right << setw(13) << "Runtime: " << left << movie.Runtime << " minutes" << endl; 
}

Movie* createDatabase(int& number_Of_Lines)
{
    // input file 
    ifstream movie_file;
    string filename;
    do 
    {
        cout << "Please enter filename: " ;
        getline (cin , filename);
        movie_file.open(filename.c_str());
        if(movie_file.fail())
            cout << "Invalid file" << endl ; 
    }while(movie_file.fail());

    // array of objects
    number_Of_Lines = numberOfLines(movie_file);
    Movie* ptr = new Movie [number_Of_Lines]; 

    //Looping through array of objects 
    for(int i = 0 ; i < number_Of_Lines ; i++)
        populateMovieFromFile(movie_file, ptr[i]);

    return ptr; 
}

bool caseInsensitiveCmp(string input, string list) //list will be from the object of array 
{
    int i = 0 , j = 0;
    while (input[i])
    {
        char c = input[i]; 
        input[i] = tolower(c); 
        i++;
    }
    while (list[j])
    {
        char c = list[j]; 
        list[j] = tolower(c); 
        j++;
    } 
    if (input == list)
        return true;    
    else 
        return false;
}

void findMovie(Movie* ptr_movie, int number_Of_Lines)
{
    cout << endl; 
    int i = 0;
    char save; 
    string input_title; 
    bool found = false; 
    bool No_Match = false;
    cout << "Enter a movie title to search for: ";
    getline(cin , input_title);
    do
    {
        found = caseInsensitiveCmp(ptr_movie[i].Title , input_title); //loop it
        if (found == false)
            i++; 
        if (i>=number_Of_Lines)
            No_Match = true;
    } while (found == false || No_Match == false);

    if(found == true)
    {
        displayMovie(ptr_movie[i]);
        cout << endl ;
        cout << "Would you like to save the above movie? (Y or N)" << endl;
        cin >> save; 
        if (save == 'y' || save == 'Y')
            saveToFile(ptr_movie[i]);
    }

    else 
        cout << input_title << " not found in database. Please try again." << endl;
}

void saveToFile(const Movie& movie)
{
    ofstream outfile; 
    outfile.open("favourites.txt", ios::app);
    cout << movie.Title << "," << movie.Gross << "," 
    << movie.Director << "," << movie.Date << "," 
    << movie.Runtime << endl;
}

bool promptToContinue()
{
    char Quit; 
    bool exit = false;
    cout << "Would you like to exit? (Y or N): ";
    cin >> Quit; 
    switch (Quit)
    {
        case 'Y':
        case 'y':
            exit = true;

        case 'N': 
        case 'n': 
            exit = false;
    }
    return exit;
}

任何帮助表示赞赏

标签: c++

解决方案


问题是:

在您的“saveToFile”函数中,您打开一个 ofstream,但不要使用它。相反,您写入 std::cout。因此,您不存储数据。

此外,您正在调用 new 但从不删除。这样一来,您正在创建内存泄漏。

然后,您仍然在 C 中思考很多。您不应该使用new或纯 C 样式数组。绝不。

您应该改为使用 STL 容器和更面向对象的方法。目前,您正在使用许多全局函数来处理数据。

在 C++ 中,您应该使用对象以及相关的数据和方法。例如,电影知道如何读取和存储其数据。因此,将其作为一种方法来实现。

电影数据库是一个包含电影向量的附加对象。

为了让您了解更面向对象的方法,我为您创建了一个小示例。

请查看并尝试理解。

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <regex>
#include <string>

class Movie
{
public:
    // Overload Extractor Operator to read data from somewhere
    friend std::istream& operator >> (std::istream& is, Movie& m);

    // Overload Inserter operator. Insert data into output stream
    friend std::ostream& operator << (std::ostream& os, const Movie& m);

    // Show movie data on std::out
    void display() const;

    // Check if a movie has a certain title
    bool hasTitle(const std::string& t) const { return t == title; }

private:
    // Data
    std::string title{};        // Movie title  
    std::string gross{};        // Gross total in billion dollars           
    std::string director{};     // Director name 
    std::string date{};         // Release date
    std::string runtime{};      // Runtime in minutes
};



// Overload Extractor Operator to read data from somewhere
std::istream& operator >> (std::istream& is, Movie& m) {
    std::vector<std::string> dataInOneLine{};       // Here we will store all data that we read in one line;
    std::string wholeLine;                          // Temporary storage for the complete line that we will get by getline
    std::regex separator(","); ;                    // Separator for a CSV file
    std::getline(is, wholeLine);                    // Read one complete line 

    // Parse the line and split it into parts
    std::copy(std::sregex_token_iterator(wholeLine.begin(), wholeLine.end(), separator, -1),
        std::sregex_token_iterator(),
        std::back_inserter(dataInOneLine));

    // If we have read all expted strings, then store them in our struct
    if (dataInOneLine.size() == 5) {
        m.title = dataInOneLine[0];
        m.gross = dataInOneLine[1];
        m.director = dataInOneLine[2];
        m.date = dataInOneLine[3];
        m.runtime = dataInOneLine[4];
    }
    return is;
}

std::ostream& operator << (std::ostream& os, const Movie& m) {
    // Copy csv data to ostream
    return os << m.title << "," << m.gross << "," << m.director << "," << m.date << "," << m.runtime << "\n";
}

void Movie::display() const {
    std::cout << "       Title: " << title << "\n Gross Total: " << gross << " billion dollars\n    Director: " << director
        << "\nRelease date: " << date << "\n     Runtime: " << runtime << " minutes\n";
}


// Database for Movies
class MovieDatabase {
public:
    // Constructor. Open and read the database
    explicit MovieDatabase(const std::string pafn) : pathAndFileName(pafn) { open(); }

    // Destructor automatically saves and closes the database
    ~MovieDatabase() { close(); };

    // Open/close the database
    bool open();
    void close();

    // Add a new movie
    void addMovie(const Movie& m) { data.push_back(m); }

    // Find and display a movie
    bool findAndDisplay (const std::string& title);

private:
    const std::string pathAndFileName{};
    std::vector<Movie> data{};
};

// Destructor
void MovieDatabase::close() {
    // Save data
    std::ofstream outFileStream{ pathAndFileName, std::ios::trunc };
    if (outFileStream) {
        // then save all data in csv format
        std::copy(data.begin(), data.end(), std::ostream_iterator<Movie>(outFileStream));
    }
}

// Open database and read the data from disk
bool MovieDatabase::open() {
    bool success{ false };
    // Open the file
    std::ifstream inFileStream{ pathAndFileName };
    // If the file could be opened
    if (inFileStream) {
        success = true;
        // Then copy all data from disk, parse the csv and store it in our data vector
        std::copy(std::istream_iterator<Movie>(inFileStream), std::istream_iterator<Movie>(), std::back_inserter(data));
    }
    return success;
}

// Find and display a value
bool MovieDatabase::findAndDisplay (const std::string& title) {

    bool found { false };
    // Search for a given title

    std::vector<Movie>::iterator md = std::find_if(data.begin(), data.end(), [&title](const Movie &m) { return m.hasTitle(title); });
    if (data.end() != md) {
        // If found, then display it
        md->display();
        found = true;
    }
    else {
        std::cerr << "\n\nTitle '" << title << "' not found in database\n\n";
    }
    return found;
}

int main() {

    // Get the name of the database
    std::string pathNameDatabase{};
    std::cout << "Enter the path/filename of the database:\n";
    std::cin >> pathNameDatabase;

    // Define database and open it
    MovieDatabase md{ pathNameDatabase };

    // Do some stuff
    std::cout << "\n\nSearch for title. Please enter title:\n";
    std::string title{};
    std::cin >> title; std::cin.ignore();
    // Search and display data
    md.findAndDisplay(title);

    // Add a new record
    std::cout << "\n\nAdd new movie data\nPlease enter title, gross, director, date, runtime (in one line, seperated by comma):\n";
    Movie m{};
    std::cin >> m;

    m.display();
    md.addMovie(m);

    return 0;
}


推荐阅读