First off, some setup. I'm doing the final course project for my C++ class, and this is the first time I've had trouble. The things I'm supposed to be doing in this assignment seemed a lot simpler before I started working on this, and now I'm just hitting walls.

The assignment is to design a program that loads records from a file, and the records contain the names of movies, the two main actors for those movies, the year of release, and the length in minutes (a list of DVDs the user supposedly owns and information about each movie). It's supposed to contain, in a class file, methods for adding new DVDs, editing information on those DVDs, and for removing a DVD. Also in a class file, it's supposed to store the DVD information that is saved and loaded from file.

I really am struggling with loading and saving from files. What I assume is that I need a data structure to hold the information and then I need to pass the structure to my main to save. The problem is that we didn't go that in-depth when we learned this, and I'm not sure how to do it. How I'm trying to do it is not working. There are tons of problems.

Here is my main.cpp file:

#include "dvdinfo.h"
#include<iostream>
#include<string>
#include<fstream>
using namespace std;

void displayMenu();
void mFileIn();

struct Movie
{
    string sTitle, sA1, sA2;
    int sYear, sTime;
};

int main ()
{
    DvdInfo dInfo;
    Movie thisDVD;
    int mChoice;
    bool doExit = false,
        doSave;
    fstream movieFile("movielist.dat", ios::out | ios::binary);

    cout << "Welcome to your Electronic DVD Library" << endl << endl;
    while (!doExit)
    {
        displayMenu();
        cout << "[?] Choice: ";
        cin >> mChoice;
        while (mChoice < 1 || mChoice > 7)
        {
            cout << "\n\n[!] Invalid option." << endl;
            cout << "[?] Choice: ";
            cin >> mChoice;
        }

        cout << endl;

        switch (mChoice)
        {
        case 1:
            cout << "No functionality yet." << endl;
            break;
        case 2:
            if (dInfo.setInfo())
            {
                thisDVD = dInfo.passInfo();
                movieFile.write(reinterpret_cast<char *>(&thisDVD), sizeof(thisDVD));
            }
            break;
        case 3:
            cout << "No functionality yet." << endl;
            break;
        case 4:
            mFileIn();
            break;
        case 5:
            cout << "No functionality yet." << endl;
            break;
        case 6:
            cout << "No functionality yet." << endl;
        case 7:
            doExit = true;
        }
    }

    system("pause");
    return 0;
}

void displayMenu() 
{
    cout << "\n|===============================================|" << endl;
    cout << "[ 1 ] Review DVD Library" << endl;
    cout << "[ 2 ] Add a new DVD" << endl;
    cout << "[ 3 ] Remove a DVD" << endl;
    cout << "[ 4 ] Add DVDs from a file" << endl;
    cout << "[ 5 ] Purge DVD library" << endl;
    cout << "[ 6 ] Save your list" << endl;
    cout << "[ 7 ] Exit" << endl << endl;
}


void mFileIn()
{
    fstream dvdFile;
    string fName, fOut, iMov, iA1, iA2;
    int iYear, iTime, search,
        isAt = 1;

    cin.get();
    cout << endl;

    cout << "Please input the name of the file you want to import from." << endl;
    cout << "[?] File name: ";
    getline(cin,fName);

    dvdFile.open(fName, ios::in);

    //See if the file will open.
    if (dvdFile.fail()) {
        cout << "[!] Error!  File not found." << endl;
    }

    //Get the line for output.
    while (dvdFile)
    {
        getline(dvdFile, fOut, '$');
        cout << fOut << endl;
    }
}

And here is my class header file, dvdinfo.h

#ifndef DVDINFO_H
#define DVDINFO_H
#include<string>
using namespace std;

class DvdInfo
{
private:
    static int dvdCount;

    struct Movie
    {
        string sTitle, sA1, sA2;
        int sYear, sTime;
    };

    Movie thisMovie;
    bool isCorrect, doSave;
    char yesno;

public:
    DvdInfo ()
    {
        thisMovie.sTitle = "empty";
        thisMovie.sA1 = "empty";
        thisMovie.sA2 = "empty";
        thisMovie.sYear = 1900;
        thisMovie.sTime = 20;
    }

    DvdInfo (string t, string a1, string a2, int y, int l)
    {
        thisMovie.sTitle = t;
        thisMovie.sA1 = a1;
        thisMovie.sA2 = a2;
        thisMovie.sYear = y;
        thisMovie.sTime = l;
    }

    Movie passInfo()
    {
        return thisMovie;
    }

    bool setInfo()
    {
        isCorrect = true;
        yesno = 'Y';

        cin.get();

        cout << endl;
        cout << "Please input the information for the DVD you wish to add." << endl;

        cout << "[?] Title: ";
        getline(cin, thisMovie.sTitle);
        cout << endl << endl;

        cout << "[?] Lead Actor: ";
        getline(cin, thisMovie.sA1);
        cout << endl << endl;

        cout << "[?] Supporting actor: ";
        getline(cin, thisMovie.sA2);
        cout << endl << endl;

        cout << "[?] Year of release: ";
        cin >> thisMovie.sYear;
        while (thisMovie.sYear < 1900 || thisMovie.sYear > 2015)
        {
            cout << "\n\n[!] Invalid year. (Please choose 1900 to 2015)" << endl;
            cout << "[?] Year of release: ";
            cin >> thisMovie.sYear;
        }
        cout << endl << endl;

        cout << "[?] Length (in minutes): ";
        cin >> thisMovie.sTime;
        while (thisMovie.sTime < 20 || thisMovie.sTime > 300)
        {
            cout << "\n\n[!] Invalid length.  (Please choose 20 to 300)" << endl;
            cout << "[?] Length (in minutes): ";
            cin >> thisMovie.sTime;
        }
        cout << endl;
        cout << "\n|===============================================|" << endl;
        cout << thisMovie.sTitle << " (" << thisMovie.sYear << ")" << endl;
        cout << "Starring " << thisMovie.sA1 << " and " << thisMovie.sA2 << endl;
        cout << thisMovie.sTime << " minutes." << endl;
        cout << "|===============================================|" << endl << endl;
        cout << "Is this correct?" << endl;
        cout << "[?] (Y)es or (N)o: ";
        cin >> yesno;
        while (toupper(yesno) != 'Y' && toupper(yesno) != 'N')
        {
            cout << "\n\n[!] Please enter 'Y' for yes or 'N' for no." << endl;
            cout << "[?] (Y)es or (N)o: ";
            cin >> yesno;
        }

        switch (toupper(yesno))
        {
        case 'Y':
            cout << "Saving the new title to the file." << endl;
            bool doSave = true;
        case 'N':
            cout << "Returning to the main menu." << endl;
            bool doSave = false;
        }
        return doSave;
    }

};
#endif

Obviously, a lot hasn't been done. I got stuck pretty early in and now I don't know what to do. I work all this week and I won't have a lot of time, and it's due on Sunday.

You will have a lot easier time reading/writing the Movie structure if you replace the std::string with char arrays, something like below. std::string can not be easily written to binary files.

In c++ program I like to use a constructor to initialize the data rather than initializing the data in some other function or class. Structures are almost identical to class in c++.

struct Movie
{
    char sTitle[126];
    char sA1[80];
    char sA2[80];
    unsigned int sYear, sTime;
    Movie()
    {
       memset(sTitle,0,sizeof(sTitle));
       memset(sA1,0,sizeof(sA1));
       memset(sA2,0,sizeof(sA2));
       sYear = sTime = 0;
    }
};

How would I pass my structure from my DvdInfo class to main? My textbook has a function in the main source file that returns the values of a temporary structure to an identical one in main called "c" using, " c = getInfo(); ". That's why I tried using " thisDVD = dInfo.passInfo(); ". It doesn't work. Visual C++ gives me an underlined = and it says "no operator "=" matches the operands". I don't understand that.

The other main issue going on is that everything at and below line 50 in my DvdInfo method "bool setInfo()" is giving me errors, including cin, cout, and <</>>. I don't understand why that would be. I didn't do anything, as far as I can tell, to break it. :\

There is a conflict between your declaration of the struct Movie in the main file and in the DvdInfo class. I would recommend that you move the struct Movie declaration out of the DvdInfo class to just above it (still in the dvdinfo.h header). And remove the declaration in main file. As so:

#ifndef DVDINFO_H
#define DVDINFO_H

#include<string>
using namespace std;

struct Movie
{
    string sTitle, sA1, sA2;
    int sYear, sTime;
};

class DvdInfo
{
private:
    static int dvdCount;

    Movie thisMovie;
    bool isCorrect, doSave;
    char yesno;
public:
    // ....
};

#endif

and the main file:

#include "dvdinfo.h"
#include<iostream>
#include<string>
#include<fstream>
using namespace std;

void displayMenu();
void mFileIn();

int main ()
{
//...
//...    

The above fix will solve your problem about thisDVD = dInfo.passInfo();.

As for the error in setInfo() about cin and cout, that error is just because you have not included the <iostream> header in your dvdinfo.h header file. So, you need this:

#ifndef DVDINFO_H
#define DVDINFO_H

#include <string>
#include <iostream>   // notice addition here.
using namespace std;

//...

1) That makes sense. I didn't even think of being able to do that, since it won't be in the main file. Works, though, so I won't complain.

2) Derp. Thank you. :) I hadn't even noticed.

I'm going to move along and try getting those things working with some of the other functions of the program. I'll probably be back. >.>

Thank you again.

Oh, look. I'm back.

I changed what was suggested. I didn't put a constructor into my data structure, since I have one in my class that affects my data structure (I'm thinking it'll do much the same?), but I did convert my strings to c-strings and updated all of the code accordingly.

The problem now lies with loading from the file (I think).

Here's my save/load code:

switch (mChoice)
        {
        case 1:
                movieFile.read(reinterpret_cast<char *>(&thisDVD), sizeof(thisDVD));
                while(!movieFile.eof())
                {
                    cout << thisDVD.sTitle << " (" << thisDVD.sYear << ")" << endl;
                    cout << "Starring " << thisDVD.sA1 << " and " << thisDVD.sA2 << endl;
                    cout << thisDVD.sTime << " minutes" << endl;

                    movieFile.read(reinterpret_cast<char *>(&thisDVD), sizeof(thisDVD));
                }
            break;
        case 2:
            if (dInfo.setInfo())
            {
                cout << "\n\nSaving the DVD information to the file..." << endl;
                thisDVD = dInfo.passInfo();
                movieFile.write(reinterpret_cast<char *>(&thisDVD), sizeof(thisDVD));
            }
            break;
        case 3:

And here's the refurbished tail-end of that problem function from before:

        cout << "\n|===============================================|" << endl;
        cout << thisMovie.sTitle << " (" << thisMovie.sYear << ")" << endl;
        cout << "Starring " << thisMovie.sA1 << " and " << thisMovie.sA2 << endl;
        cout << thisMovie.sTime << " minutes." << endl;
        cout << "|===============================================|" << endl << endl;
        cout << "Is this correct?" << endl;
        cin.get();
        cout << "[?] (Y)es or (N)o: ";
        cin >> yesno;
        while (toupper(yesno) != 'Y' && toupper(yesno) != 'N')
        {
            cout << "\n\n[!] Please enter 'Y' for yes or 'N' for no." << endl;
            cout << "[?] (Y)es or (N)o: ";
            cin >> yesno;
        }

        switch (toupper(yesno))
        {
        case 'Y':
            //cout << "Saving the new title to the file." << endl;
            doSave = true;
            break;
        case 'N':
            cout << "Returning to the main menu." << endl;
            doSave = false;
        }
        return doSave;
    }

And now, here's what's happening:
http://oi46.tinypic.com/2dkikjq.jpg

It repeats that last block 21 times before displaying the menu again.

Eventually I'm going to have it load from the file and save all that info to a vector array of DVDs, but.. Not like that. :D

You cannot use this type of thing:

movieFile.read(reinterpret_cast<char *>(&thisDVD), sizeof(thisDVD));

this is bad. You cannot just dump the memory of "thisDVD" into a file like that. You need to create a member function in the DvdInfo class that will save / load the file by saving individual data members. Something like this:

class DvdInfo {
  // ....

  void saveToFile(ofstream& OutStream) {
    OutStream.write(reinterpret_cast<const char*>(thisMovie.sTitle), strlen(thisMovie.sTitle) + 1);  // the '+ 1' is for the null-termination character.
    OutStream.write(reinterpret_cast<const char*>(thisMovie.sA1), strlen(thisMovie.sA1) + 1);  // the '+ 1' is for the null-termination character.
    OutStream.write(reinterpret_cast<const char*>(thisMovie.sA2), strlen(thisMovie.sA2) + 1);  // the '+ 1' is for the null-termination character.

    OutStream.write(reinterpret_cast<const char*>(&thisMovie.sYear),sizeof(int));
    OutStream.write(reinterpret_cast<const char*>(&thisMovie.sTime),sizeof(int));

    // ...
  };

  void loadFromFile(ifstream& InStream) {
    InStream.getline(thisMovie.sTitle, 126, '\0');
    InStream.getline(thisMovie.sA1, 80, '\0');
    InStream.getline(thisMovie.sA2, 80, '\0');

    InStream.read(reinterpret_cast<const char*>(&thisMovie.sYear),sizeof(int));
    InStream.read(reinterpret_cast<const char*>(&thisMovie.sTime),sizeof(int));

    // ...
  };

};

But, in general, I recommend saving and loading into a text format (not binary), because it's less trouble that way (and you can open the file in a text editor to see what it produced). And, of course, you call the save-load functions like this:

switch (mChoice)
    {
    case 1:
            thisDVD.loadFromFile(movieFile);
            while(!movieFile.eof())
            {
                cout << thisDVD.sTitle << " (" << thisDVD.sYear << ")" << endl;
                cout << "Starring " << thisDVD.sA1 << " and " << thisDVD.sA2 << endl;
                cout << thisDVD.sTime << " minutes" << endl;

                thisDVD.loadFromFile(movieFile);
            }
        break;
    case 2:
        if (dInfo.setInfo())
        {
            cout << "\n\nSaving the DVD information to the file..." << endl;
            thisDVD = dInfo.passInfo();
            thisDVD.saveToFile(movieFile);
        }
        break;
    case 3:

And you should also set the ios::in flag as well as the ios::out | ios::binary if you are going to use the file to do both input and output.

With thisDVD.saveToFile and loadFromFile, it's saying that my structure has no function called saveToFile or loadFromFile, since thisDVD is a Movie object, and Movie is my data structure. I changed it to dInfo.saveToFile(movieFile) and dInfo.loadFromFile(movieFile), since my class can access the variables in the structure, and now it's underlining movieFile and telling me that... "a reference of type 'std::ifstream &' (not const-qualified) cannot be initialized with a value of type 'std::fstream'".

I'm changing the functions to say fstream.. Seeing if that helps.

1>c:\users\chris\documents\visual studio 2010\projects\course project\course project\dvdinfo.h(120): error C2027: use of undefined type 'std::basic_fstream<_Elem,_Traits>'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>
1>          ]
1>c:\users\chris\documents\visual studio 2010\projects\course project\course project\dvdinfo.h(120): error C2228: left of '.getline' must have class/struct/union
1>c:\users\chris\documents\visual studio 2010\projects\course project\course project\dvdinfo.h(121): error C2027: use of undefined type 'std::basic_fstream<_Elem,_Traits>'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>
1>          ]
1>c:\users\chris\documents\visual studio 2010\projects\course project\course project\dvdinfo.h(121): error C2228: left of '.getline' must have class/struct/union
1>c:\users\chris\documents\visual studio 2010\projects\course project\course project\dvdinfo.h(122): error C2027: use of undefined type 'std::basic_fstream<_Elem,_Traits>'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>
1>          ]
1>c:\users\chris\documents\visual studio 2010\projects\course project\course project\dvdinfo.h(122): error C2228: left of '.getline' must have class/struct/union

It didn't help. :( I don't question your code; rather, I'm uncertain of my ability to implement it.

Oh, sorry, it should be istream and ostream, instead of ifstream and ofstream. That should fix it.

Awesome. :) There's only one more issue and I might be able to handle it. I'll just need to create a vector array of DVD objects from the file, etc.

The problem is with the code to load.

    void loadFromFile(istream& InStream) 
    {
        InStream.getline(thisMovie.sTitle, 126, '\0');
        InStream.getline(thisMovie.sA1, 80, '\0');
        InStream.getline(thisMovie.sA2, 80, '\0');
        InStream.read(reinterpret_cast<const char*>(&thisMovie.sYear),sizeof(int));
        InStream.read(reinterpret_cast<const char*>(&thisMovie.sTime),sizeof(int));
    };

reinterpret_cast is underlined red and it says type const char* is incompatible with type char*. And nevermind. I fixed it and it's not a const anymore. :D

Huzzah!

And now a new problem. D:

It loads the movie I added... infinite numbers of times. It loads and outputs correctly, but... something's happening where it doesn't read the end of the file happening and just keeps looping, loading the same movie over and over and over... And when I close out and try it again, it doesn't output anything. :\ And that's without altering any of the code before trying again.

..And now it's just looping every time.

I just changed ios::out to ios::app so it could add movies to the end of the file instead of replacing all the content with a single set of information, but when I try to review the full list, using the below code, it just loops the most recent addition. I can't tell if it's adding anything. When I go to open the file after closing the program, it's been created (I deleted it before running the program again), but nothing has been written to it.

In main:

    fstream movieFile;

    movieFile.open("movielist.txt", ios::app | ios::in);

Case 1(loading and outputting):

            dInfo.loadFromFile(movieFile);
            while(!movieFile.eof())
            {
                cout << thisDVD.sTitle << " (" << thisDVD.sYear << ")" << endl;
                cout << "Starring " << thisDVD.sA1 << " and " << thisDVD.sA2 << endl;
                cout << thisDVD.sTime << " minutes" << endl;
                dInfo.loadFromFile(movieFile);
            }
            break;

The loadFromFile function:

    void loadFromFile(istream& InStream) 
    {
        InStream.getline(thisMovie.sTitle, 126, '\0');
        InStream.getline(thisMovie.sA1, 80, '\0');
        InStream.getline(thisMovie.sA2, 80, '\0');
        InStream.read(reinterpret_cast<char*>(&thisMovie.sYear),sizeof(int));
        InStream.read(reinterpret_cast<char*>(&thisMovie.sTime),sizeof(int));
    };

Whilst awaiting any assistance available on those issues, I decided to go ahead and try to set up the vectorization of my dInfo object(s). I start off with a declaration of vector<DvdInfo> dInfo; in main and, in case one, what I was planning to do was dInfo.push_back(); to create a new instance of a DvdInfo object in my vector, then dInfo[dInfo.size()].loadFromFile(movieFile); to ensure that the information loaded went into the last element, and then I could handle the whole process of saving, loading, and displaying information to/from specific DvdInfo objects pretty easily.

That was the plan.

It's got the period between dInfo and push_back underlined and is saying:
no instance of the overloaded function "std::vector<_Ty, _Ax>::push_back[_Ty=DvdInfo, _Ax=std::allocator<DvdInfo>]" matches the argument list

:\ I keep thinking things will be simpler than they really will be.

To your previous problem, I don't really know what is going on. But I would generally recommend that you don't use only one file-stream for reading and writing. I think that is where the problems might be coming from. Generally, you should use a ofstream for output operations, and ifstream for the input operations. You probably should go about it something like this:

  • Keep all the records (dInfo) in a vector.
  • When asked to save all the records to a file, then,

    • Open the file using a ofstream, with flags ios::binary | ios::out.
    • Write the entire vector of records into the file.
  • When asekd to load all the records from a file, then,

    • Open the file using a ifstream, with flags ios::binary | ios::in.
    • Clear the current vector of records (or keep it temporarily).
    • Read all the records from the file into a vector.
    • (Merge the vector of records from the file with the pre-existing record vector, eliminating duplicates).

That's going to be easier the manage. Then, all your other operations can be done on that vector of records (not through constantly reading and writing to a file). Also, using the input-stream for inputs and the output-stream for outputs will give you a better chance at avoiding the issues you have with the end-of-file marker and stuff.

It's got the period between dInfo and push_back underlined and is saying:

If you look at the reference website for push_back(), you'll see that, as the compiler says, there is no version of push_back() that takes no argument. You must provide an object to copy into the new element of the vector. The usual way to do this would be:

DvdInfo tmp_dvd;                  // create a temporary dvd-info record.
tmp_dvd.loadFromFile(movieFile);  // load the record from the file.
dInfo.push_back(tmp_dvd);         // add the record to the vector (you might also check that it was loaded successfully before adding it to the vector).

Part 2: I see. That makes sense, and it does, in fact, work. Thank you.

Part 1: That's sort of what I was going to do. I didn't know having more than one or only one filestream object would matter, though.

Anywho, I've changed the code at the start of my main function to say:

    ifstream inMovie;
    ofstream outMovie;
    vector<DvdInfo> dInfo;
    DvdInfo tmp_Dvd;

    //Populate vector with information from the file
    inMovie.open("movielist.txt", ios::in | ios::binary);
    dInfo.clear();
    tmp_Dvd.loadFromFile(inMovie);
    while(!inMovie.eof())
        {
            dInfo.push_back(tmp_Dvd);
            tmp_Dvd.loadFromFile(inMovie);
        }
            inMovie.close();

I can't tell yet if that works, because I need to rewrite the save, since I'm not saving when I add a singular new DVD to the list now. Instead, for "case 2" I've put:

        case 2:
            if (tmp_Dvd.setInfo())
            {
                cout << "\n\nAdding DVD to list.  Don't forget to save." << endl;
                thisDVD = tmp_Dvd.passInfo();
                dInfo.push_back(tmp_Dvd);
            }
            break;

That seems to work. When I run "case 1", I have a code that outputs the size of the vector, and after adding new DVDs, the number climbs. However, it's not outputting DVD information.

        case 1:
            for(int index = 0; index > dInfo.size(); index++)
            {
                dInfo[index].printInfo();
            }
            cout << "Number of DVDs: " << dInfo.size() << endl;
            break;

and my accompanying class method:

    void printInfo()
    {
        cout << thisMovie.sTitle << " (" << thisMovie.sYear << ")" << endl;
        cout << "Starring " << thisMovie.sA1 << " and " << thisMovie.sA2 << endl;
        cout << thisMovie.sTime << " minutes" << endl;
    }

I don't see the error in that, but all I get is the output of the number of DVDs(number of vector elements). Nothing else.

EDIT: Uh. Greater than sign backwards. Checking to see if that's the problem.
EDIT 2: What do you know. I did it right, but my idiocy got in the way.

Everything... is working so well... I want to finish it now and I don't want to go to work. :(

I'll wait to "close this out" until I've finished, as I'm a moron and will probably run into additional problems.

I'll also save my thanks for when I'm done. There will be many.

I turned it in. It does everything my professor asked, so hopefully he grades it favorably.

mike_2000_17, you are a saint. Thank you for your patience, insight, and advice. I appreciate it beyond words. Thank you, thank you, thank you.

If you are curious how it turned out, here it is:
http://pastie.org/5525202 <--- main.cpp
http://pastie.org/5525206 <--- dvdinfo.h (class file)

EDIT: If you compile and run it, you'll need a movielist.txt in the same folder.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.