0

I can get my file to load 1 full struct. But when I try to iterate through the file, i have an endless loop and no data is loaded. I have attached my code and the flat file.

#include <iostream>
#include <string.h>
#include <vector>
#include <fstream>

using namespace std;

typedef struct video_items{
    string Title;
    int YOP;
    string Category;
    float Gross;
}video;

void LoadMovies (vector<video> &v);
void SearchMovies (vector <video> &v, string s);

int main()
{
    vector <video> v;

    LoadMovies(v);

    cout<<"Total number of movies: "<<v.size()<<endl;

    for (unsigned int i=0; i<v.size(); ++i) {
        cout<<"----------------------------------------------------"<<endl;
        cout<<v[i].Title<<endl;
        cout<<"Category: "<<v[i].Category<<endl;
        cout<<"Year of Publication: "<<v[i].YOP<<endl;
        cout<<"Gross: "<<v[i].Gross<<endl;
        cout<<"----------------------------------------------------"<<endl<<endl;
    }

    string WantMovie;
    cout<<"Please type in what movie you want."<<endl;
    cin>>WantMovie;
    SearchMovies(v,WantMovie);

    return 0;
}

void LoadMovies (vector<video> &v)
{
    video L;
    ifstream myfile;
    myfile.open ("Movies.txt");
    if (myfile.is_open())
    {
        cout <<"Loading Movie Catalog..."<<endl<<endl;
        int i=0;
        while (!myfile.eof())
        {
            myfile.ignore();
            //cout<<i<<endl<<endl;

            v.push_back(L);
            getline(myfile,v[i].Title);
            getline(myfile,v[i].Category);
            myfile>>v[i].YOP;
            myfile>>v[i].Gross;
            i++;
        }
        myfile.close();
    }
    else cout<<"Unable to open file."<<endl;
}

void SearchMovies (vector <video> &v, string s)
{
    s[0] = toupper(s[0]);
    unsigned int i;

    for (i=0; i<v.size(); i++)
    {
        if (v[i].Title.compare(0,s.size(),s)==0)
        {
            break;
        }
    }
    if (i >=v.size()){
        i =0;
    }
    switch(i)
    {
        case 1:cout<<"You have chosen "<<s<<endl;
        break;
        default:
        cout<<"That movie is not currently in the library, please choose a different one."<<endl;
        break;
    }
}

First character ignored in flat file.

=========Data File==========

 Captain America: The First Avenger
Action, Adventure, Sci-Fi
2011
1786.65
Iron Man
Action, Adventure, Sci-Fi
2008
585.2
The Incredible Hulk
Action, Adventure, Sci-Fi
2008
134.52
Iron Man 2
Action, Adventure, Sci-Fi
2010
312.43
Thor
Action, Adventure, Fantasy
2011
181.03
The Avengers
Action, Adventure, Sci-Fi
2012
623.28
Iron Man 3
Action, Adventure, Sci-Fi
2013
409.01
David C. Rankin
  • 69,681
  • 6
  • 44
  • 72
Deeda66
  • 1
  • 1
  • 2
    And when you ran your program in your debugger, what observations did you make? This is a good opportunity to learn how to use it to run your programs one line at a time, inspect all variables and their values as they change, and analyze your programs' logical execution flow. Knowing how to use a debugger is a required skill for every C++ developer, no exceptions. With your debugger's help you should be able to quickly find the bug in this and all future programs you write, without having to ask anyone for help. – Sam Varshavchik Nov 20 '19 at 16:21
  • 4
    [Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons) – R Sahu Nov 20 '19 at 16:23

1 Answers1

0

If you are still stuck, then think about what you have to accomplish to read each struct from your datafile. You need to read 4 pieces of information, 2-strings, 1-int, 1-float. You are thinking correctly to read into a temporary struct L, but what you want to do is validate you have correctly filled all four members of L before adding L to your vector.

To accomplish a successful read of all four members, you must first read the first-member. Condition your read-loop on reading the first member, e.g.:

    while (getline (myfile, L.Title)) {         /* validate each read */

After having read the first member, validate each of the next three members being read:

        if (getline (myfile, L.Category) &&
            myfile >> L.YOP && myfile >> L.Gross) {
            v.push_back(L);     /* before adding temp struct to vector */
            i++;
        }

It's risky to presume you only have a single '\n' following Gross in your input file and makes your read fragile. Ignore all characters to end of line, e.g.

        /* ignore all characters to \n */
        myfile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

Do not use Magic Numbers or Hardcode Filenames in your code. main() takes arguments which allows you to pass a filename to read data from on the command line (or you can take it as input), but don't bury a hardcoded filename down in a funciton:

    myfile.open ("Movies.txt");

Instead, pass the filename on the command line, e.g.

int main (int argc, char **argv)
{
    if (argc < 2) {  /* validate at least 1 argument given for filename */
        std::cerr << "usage: " << argv[0] << " filename\n";
        return 1;
    }
    ...
    LoadMovies(v, argv[1]);   /* pass filename to open to LoadMovies */

There is no reason for six calls to std::cout to display each movie. One will do:

    for (unsigned int i=0; i<v.size(); ++i)
        std::cout << "----------------------------------------------------\n"
                  << v[i].Title << '\n'
                  << "Category: " << v[i].Category << '\n'
                  << "Year of Publication: " << v[i].YOP << '\n'
                  << "Gross: " << v[i].Gross << '\n'
                  << "----------------------------------------------------\n\n";

Have SearchMovies() provide a meaningful return to indicate success/failure back in the caller. You ideally want to separate your implementation (your logic/calculations) from your interface (your output to the user). If you provide a meaningful return type for SearchMovies() you can remove the output from the function completely and have that provided back in main() based upon your return. A simple return of type int is fine. -1 to indicate not found in library, or return the index in your vector on success. Example:

int SearchMovies (std::vector<video>& v, const std::string& s)
{
    int i;
    std::string slower (s);                 /* string s to lowercase */
    std::transform (slower.begin(), slower.end(), 
                    slower.begin(), 
                    [](unsigned char c){ return std::tolower(c); });


    for (i = 0; i < (int)v.size(); i++) {   /* movie title to lowercase */
        std::string mlower (v[i].Title);
        std::transform (mlower.begin(), mlower.end(), 
                        mlower.begin(), 
                        [](unsigned char c){ return std::tolower(c); });

        if (mlower == slower)   /* compare lowercase movie and string */
            return i;
    }

    return -1;  /* if not found, return -1 (cannot be an index) */
}

(note: you can loop converting each character to lowercase, or you can just use std::transform to convert all characters.)

By converting both the movie title as well as your WantMovie search string, you can make an comparison in your search function.

Then in main() you can simply output the movie title at that index within your vector v, e.g.

    std::cout << "Please type in what movie you want.\n";
    if (getline (std::cin, WantMovie)) {
        int index = SearchMovies (v, WantMovie);
        if (index != -1)    /* validate search and output here */
            std::cout << "You have chosen: " << v.at(index).Title << '\n';
        else
            std::cout << WantMovie << " is not currently in the library.\n";
    }

Your search will then work regardless of the case the user uses to type in the search term, e.g.

...
Please type in what movie you want.
iron man 3
You have chosen: Iron Man 3

or

...
Please type in what movie you want.
thor
You have chosen: Thor

That should get you pointed in the right direction. If you run into additional stumbling blocks, let me know and I'm happy to help further.

David C. Rankin
  • 69,681
  • 6
  • 44
  • 72