无法填充结构数组

Posted

技术标签:

【中文标题】无法填充结构数组【英文标题】:Unable to populate array of structures 【发布时间】:2019-10-17 22:40:55 【问题描述】:

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

示例: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;

感谢任何帮助

【问题讨论】:

似乎没有正确存储它?这是什么意思?您的程序是否正常运行和退出?如果您的程序意外终止,这不是一件好事,您可以忘记程序是如何存储数据的。你说一个错误,这意味着你的代码是crashing。你的代码在哪一行失败了? 【参考方案1】:

问题是:

在您的“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;


【讨论】:

以上是关于无法填充结构数组的主要内容,如果未能解决你的问题,请参考以下文章

在检查新行时使用 fscanf 从文件中填充结构数组(反馈)

如何在 C 中声明结构数组

如何获取这些数组并使用它们来填充结构的字段?

使用 javascript 和 jquery,用数组结构填充相关的选择框

如何合并数组中结构的不同模式(用空填充缺失的列)?

在一个操作中声明后在结构中填充数组的最简单方法[重复]