Parsing a text file for a DVD project

Hey guys, I'm having trouble getting some information to parse properly for the code below. I'm a novice so I may ask dumb questions.

My input file looks like this:

|Highlander|1986|Fantasy|1hr 56min|R|$1.99|6|2|4|
|Stripes|1981|Comedy|1hr 46min|R|$1.99|3|2|1|
|Captain America: Civil War|2016|Action|2hr 27min|PG-13|$3.99|10|7|3|
|Deadpool|Year: 2016|Action-Comedy|1hr 48min|R|$3.99|10|10|0|
|The Matrix|Year: 1999|Sci-Fi|2hr 16min|R|$1.99|5|1|4|
|Inception|Year: 2010|Sci-Fi|2hr 28min|PG-13|$3.99|4|0|4|
|Pulp Fiction|Year: 1994|Crime|2hr 34min|R|$1.99|4|2|2|
|A Bridge Too Far|Year: 1977|War|2hr 55min|PG|$1.99|2|0|2|
|Finding Dory|Year: 2016|Family|1hr 37min|PG|$3.99|8|5|3|
|Fight Club|Year: 1999|Drama|2hr 19min|R|$1.99|6|3|3|

What I need to do is parse each line so that they display in the 9 fields I have created below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <cstdio>
#include <cstdlib>
using namespace std;

//Class declarations and setup for DVD class
class DVD {

    //properties for a DVD
    private:                //I know classes default to private but good habit to write it out

        string dvdTitle;       //The name of the movie
        string dvdYear;        //The year the movie was made
        string dvdGenre;       //The genre of the movie
        string dvdRuntime;     //The runtime of the movie
        string dvdRating;      //The rating of the movie
        string rentalPrice;    //The price of the rental (usually has a premium/new or classic/old number)
        string dvdOwned;       //The number of DVD copies the store owns
        string dvdRented;      //The number of copies currently rented
        string dvdAvail;       //The number of copies on the shelf

    //Member functions for a DVD class, they can be voided
    public:

        //DVD Title
        void setdvdTitle (string a){
            dvdTitle = a;
        }
        string getdvdTitle(){
            return dvdTitle;
        }

         //DVD Year
         void setdvdYear (string b){
            dvdYear = b;
        }
        string getdvdYear(){
            return dvdYear;
        }

        //DVD Genre
        void setdvdGenre (string c){
            dvdGenre = c;
        }
        string getdvdGenre (){
            return dvdGenre;
        }
        //DVD Runtime
        void setdvdRuntime (string d){
            dvdRuntime = d;
        }
        string getdvdRuntime (){
            return dvdRuntime;
        }
        //DVD Rating
        void setdvdRating (string e){
            dvdRating = e;
        }
        string getdvdRating (){
            return dvdRating;
        }

        //DVD rental price
        void setrentalPrice (string f){
            rentalPrice = f;
        }
        string getrentalPrice(){
            return rentalPrice;
        }

        //DVD copies owned
        void setdvdOwned (string g){
            dvdOwned = g;
        }
        string getdvdOwned(){
            return dvdOwned;
        }

        //DVD copies rented out
        void setdvdRented (string h){
            dvdRented = h;
        }
        string getdvdRented(){
            return dvdRented;
        }

        //DVD copies available on shelf
        void setdvdAvail (string i){
            dvdAvail = i;
        }
        string getdvdAvail(){
            return dvdAvail;
        }

    //Read one DVD from file
    bool Read (istream & is)
    {   getline (is, dvdTitle);
        getline (is, dvdYear);
        getline (is, dvdGenre);
        getline (is, dvdRuntime);
        getline (is, dvdRating);
        getline (is, rentalPrice);
        getline (is, dvdOwned);
        getline (is, dvdRented);
        getline (is, dvdAvail);
        return is.good();
    }

    //  Print one DVD
    void Print (ostream & os) const
    {   os << "Title: " << dvdTitle << endl;
        os << "Year: " << dvdYear << endl;
        os << "Genre: " << dvdGenre << endl;
        os << "Runtime: " << dvdRuntime << endl;
        os << "Rating: " << dvdRating << endl;
        os << "Rental Price: " << rentalPrice << endl;
        os << "Copies Owned: " << dvdOwned << endl;
        os << "Copies Rented: " << dvdRented << endl;
        os << "Copies Available: " << dvdAvail << endl;
    }

       };

 int main()
{   DVD dvdArr[10];
    ifstream filename("DVDlist.txt");    //  Declare and open the file of DVDs

    for(int x = 0; x<10; x++)       //  Read one DVD from the file
    {
      dvdArr[x].Read (filename);
      dvdArr[x].Print (cout);

       }
    return 0;
}


As it stands now (output isn't working for this thread for some reason), it prints my array but just puts the 10 full lines on the first 10 lines, and then the other 9 arrays are empty. I know I need to declare the delimiter "|" but I have no idea how to do that in a simple way, especially within the context of the code above.

A rough look at the output follows:


Title: Highlander|1986|Fantasy|1hr 56min|R|$1.99|6|2|4|
Year: Stripes|1981|Comedy|1hr 46min|R|$1.99|3|2|1|
Genre: Captain America: Civil War|2016|Action|2hr 27min|PG-13|$3.99|10|7|3|

etc. (for 10 lines)

Title:
Year:
Genre:

etc. for the remainder of the 9 arrays


Also I want to credit AbstractionAnon for helping me get this far, with his help in another person's thread a few months ago.
I know I need to declare the delimiter "|" but I have no idea how to do that in a simple way, especially within the context of the code above.

Did you find and read the documentation for getline()?

Did you notice that there are three parameters you can use with this function?

Did you try using all three parameters?

http://www.cplusplus.com/reference/string/string/getline/
Lines 100-108: getline (if the third parameter is not specified) will read to '\n'. This means your first getline at line 100 will read the entire line into dvdTitle. The next getline will read the second line into dvdYear, etc.

I provided you a parse_string() function via PM that will break apart a string based on the '|' separator between fields. I would have hoped that you would have incorporated that routine in the code you just posted. It seems I'm repeating everything here that I said in my last PM.

Your for loop requires that you know exactly how many DVDs are in the file. If the number of DVDs changes, you have to recompile your program.

Here's the code again, adjusted for all member variables being strings.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <sstream>
#include <cstdlib>
using namespace std;

class DVD
{   string dvdTitle; //The name of the movie
    string dvdYear; //The year the movie was made
    string dvdGenre; //The genre of the movie
    string dvdRuntime; //The runtime of the movie
    string dvdRating; //The rating of the movie
    string rentalPrice; //The price of the rental
    string dvdOwned; //The number of DVD copies the store owns
    string dvdRented; //The number of copies currently rented out
    string dvdAvail; //The number available for rent
    
    void parse_line (const string & line);
    
public: 
    bool Read (istream & is);
        
};

//  Parse a '|' delimited string from the stringstream
string parse_string (stringstream & ss)
{   string      fld;

    getline (ss, fld, '|');  // read up to field separator
    return fld;
}

//  Highlander|1986|Fantasy|1hr 56min|R|$1.99|6|2|4
void DVD::parse_line (const string & line)
{   stringstream    ss (line);
    
    dvdTitle = parse_string (ss);                 
    dvdYear = parse_string (ss); 
    dvdGenre = parse_string (ss);  
    dvdRuntime = parse_string (ss);    
    dvdRating = parse_string (ss);
    rentalPrice = parse_string (ss); 
    dvdOwned = parse_string (ss);
    dvdRented = parse_string (ss);
    dvdAvail = parse_string (ss);
}       
    
//  Read one DVD from the file 
//  Return false at EOF    
bool DVD::Read (istream & is)
{   string      line;
    
    if (! getline (is, line))   //  Read up to \n
        return false;           //  Did not get a line
    parse_line (line);          //  break the line apart
    return true;                //  Got a DVD 
}    


BTW, you don't need dvdAvail. dvdAvail can always be calculated as (ignoring the fact that these are now strings):
 
  dvdAvail = dvdOwned - dvdRented;


Last edited on
@ Jlib, I did not. Thank you! I will read it now. (Edit: on that link it only lists 2 parameters btw)

@ Anon, you are correct, please see my last PM. I did incorporate most of that code for parsing, (even managed to change most of it as you have done with the strings). I especially focused on the delimiter parse segments (I think there were two?).

I even managed to get it to compile but the issue remained. By the time I had "debugged" (that's using that term correctly?), however, I had literally destroyed your code and so I posted in the open thread as suggested in your original PM to bring more people in and take the burden off you.

It's my first time posting and even though it shows the "code" and "output" windows, they weren't displaying in the "preview" (preview was completely empty) so I went back and used the cleanest/working source code I had. You are correct that you did provide parse code, and I did test it rigorously, I promise you, but if I addressed one line, 5 more errors from my compiler would pop up. By the time I was done, I had only about 9 lines left of your original code.

For instance, I also had done the manual changes to lines 36-44 too (part of the changes I made) but wasn't sure if I had done it right (followed your examples).

Another issue was line 33 and 49 keep giving me issues with the DVD:: part. My IDE was saying they were redundant.

Also "void parse_line (const string & line);" was throwing errors, but I managed to correct them (I forget how now) and it compiled.

As I mentioned in the PM, I had a hard time understanding alot of it but spent a few hours reading up line by line. If you recall, I had mentioned that I didn't know how/where to slot all the code in exactly (or more accurately, what replaced what, or if it all compounded together). I was simply of out my depth and didn't want to keep harassing you in PMs to "explain more".

For example, if I take this code above wholesale that you've just provided, I get the error "undefined reference to WinMain@16" which I have no clue what it means since that isn't declared anywhere in your code, so my IDE is making stuff up.

EDIT 2: Oh geez, I'm an idiot, I need to add my main() function under it.

EDIT 3: It says I'm missing my print declaration, so I assume I add that back in, however when I include:
1
2
3
4
5
6
7
8
9
10
11
12
13
//  Print one DVD
    void Print (ostream & os) const
    {   os << "Title: " << dvdTitle << endl;
        os << "Year: " << dvdYear << endl;
        os << "Genre: " << dvdGenre << endl;
        os << "Runtime: " << dvdRuntime << endl;
        os << "Rating: " << dvdRating << endl;
        os << "Rental Price: " << rentalPrice << endl;
        os << "Copies Owned: " << dvdOwned << endl;
        os << "Copies Rented: " << dvdRented << endl;
        os << "Copies Available: " << dvdAvail << endl;
    }


I get told that none of those "dvdXXXX" are declared in the scope. I even tried adding a "void Print (ostream & os) const;" line to the member functions of the class too.

I promise you I spent ~7 hours working with your input plus a couple more this morning after chatting. Perhaps I should have done this step by step so that you can walk along with me through the errors, instead of me butchering it all and going backwards. I apologize and realize it must appear frustrating.

Also for the for loop, that part I did understand, but the instances are limited 10 dvds permanently, they are simply used to demonstrate that my class characteristics are created right. Don't worry, I took the while loop idea into memory and "learned" that for next time since it seems like a smart idea to "practice" for practical applications but is not needed this time.

And finally, you are correct about the operation for dvdAvail. It's so simple and obvious but I never even thought of it.
Last edited on
on that link it only lists 2 parameters

That link lists two calling sequences (C++98 tab) for getline ().
(1) is the three parameter form.
(1) istream& getline (istream& is, string& str, char delim);


I was simply of out my depth

Post questions for what you don't understand. Assuming you've made a reasonable effort to read up on what's been posted, someone will be glad to answer your questions.

It says I'm missing my print declaration, so I assume I add that back in?

Yes.




Last edited on
I see it now, the "char delim" is listed for both. I was confused by its layout for a second. It's mostly the &'s that make me go cross eyed (they're pointers I think?) but I still don't quite understand them properly. It points to memory locations, but I thought memory was dynamic in modern things? like you don't have to point specifically to memory, it automatically allocates the size as you call something?

Also what does "is" mean? I see it is listed under the parameters section as "istream object from which characters are extracted." Does that mean when you use it in the getline, that it is declared? I think the issue I'm having is that I always think everything has to be declared near the top, but I'm hazy on when it can be declared "in a function". Are we using "ss" in place of "is" here for the code below?

Back on track so here's where I'm at now (see bottom for discussion on what is reported in the error log and what I THINK I need to do to fix things):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <fstream>
#include <string>
#include <cstdio>
using namespace std;

class DVD
{   string dvdTitle; //The name of the movie
    string dvdYear; //The year the movie was made
    string dvdGenre; //The genre of the movie
    string dvdRuntime; //The runtime of the movie
    string dvdRating; //The rating of the movie
    string rentalPrice; //The price of the rental
    string dvdOwned; //The number of DVD copies the store owns
    string dvdRented; //The number of copies currently rented out
    string dvdAvail; //The number available for rent

    void parse_line (const string & line);

public:
    bool Read (istream & is);
};

//  Parse a '|' delimited string from the stringstream
string parse_string (stringstream & ss)
{   string      fld;

    getline (ss, fld, '|');  // read up to field separator
    return fld;
}

//  Highlander|1986|Fantasy|1hr 56min|R|$1.99|6|2|4
void DVD::parse_line (const string & line)
{   stringstream    ss (line);

    dvdTitle = parse_string (ss);
    dvdYear = parse_string (ss);
    dvdGenre = parse_string (ss);
    dvdRuntime = parse_string (ss);
    dvdRating = parse_string (ss);
    rentalPrice = parse_string (ss);
    dvdOwned = parse_string (ss);
    dvdRented = parse_string (ss);
    dvdAvail = parse_string (ss);
}

//  Read one DVD from the file
//  Return false at EOF
bool DVD::Read (istream & is)
{   string      line;

    if (! getline (is, line))   //  Read up to \n
        return false;           //  Did not get a line
    parse_line (line);          //  break the line apart
    return true;                //  Got a DVD
}

//  Print one DVD
void Print (ostream & os) const
{   os << "Title: " << dvdTitle << endl;
    os << "Year: " << dvdYear << endl;
    os << "Genre: " << dvdGenre << endl;
    os << "Runtime: " << dvdRuntime << endl;
    os << "Rating: " << dvdRating << endl;
    os << "Rental Price: " << rentalPrice << endl;
    os << "Copies Owned: " << dvdOwned << endl;
    os << "Copies Rented: " << dvdRented << endl;
    os << "Copies Available: " << dvdAvail << endl;
}

 int main()
{   DVD dvdArr[10];
    ifstream filename("DVDlist.txt");    //  Declare and open the file of DVDs

    for(int x = 0; x<10; x++)       //  Read one DVD from the file
    {
      dvdArr[x].Read (filename);
      dvdArr[x].Print (cout);

     system ("Pause");
    }

    return 0;
}


My print function is throwing about 20 errors, although if I understand my error log right, it's most just 3 errors with the rest being duplicates so I'll try not to repeat any.

||=== Build file: "no target" in "no project" (compiler: unknown) ===|

61|error: non-member function 'void Print(std::ostream&)' cannot have cv-qualifier|
-------------------------------------------------------------------------------------------------------------------------------
In function 'void Print(std::ostream&)':|

62|error: 'dvdTitle' was not declared in this scope|

<This error is repeated until line 70 below for each>

70|error: 'dvdAvail' was not declared in this scope|
------------------------------------------------------------------------------------------------------------------------------
|In function 'int main()':|

80|error: 'class DVD' has no member named 'Print'|

||=== Build failed: 11 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|


So when I had this error earlier I tried:
Line 61: I thought it was an issue with DVD:: (ie. like DVD::Read () ) in previous builds. To fix I added DVD:: to the Print line.

Line 62 -70: No idea how to fix this because these are defined in my class. I think if I add DVD:: to the Print function, it'll address the scoping issues of the properties. But I want to be sure this is the right approach before I do it.

Line 80: I added a void Print () function to my class, but I might have done it wrong. Just a simple "void Print(ostream & os) const" line should work here??? (also why do we add the "const"?

Also I'll add the expression for dvdAvail when it all prints properly, want to keep it simple for now.


Edit 2: Omg it worked! I tried those changes above and it populated properly and worked!
Thank you Anon you're literally my hero! Thank you for your help (and also for helping to build my understanding along the way). You too jlb!

Only 7 more projects to go in less than 2 weeks :S
Last edited on
I see it now, the "char delim" is listed for both.

No. delim is is applicable to (1).

It's mostly the &'s that make me go cross eyed (they're pointers I think?)

& indicates the parameter is passed by reference, rather than by value. This means when updates are made to the object instance, they appear in the caller's instance.

Also what does "is" mean?

"is" is simply an argument name.

the issue I'm having is that I always think everything has to be declared near the top

Objects should be declared as locally as makes sense.

Are we using "ss" in place of "is" here for the code below?

Yes. stringstream derives from iostream, therefore anything you can do with an istream, you can do with a stringstream. Again, ss is just a variable name referring to the stringstream.

My print function is throwing about 20 errors,

Line 61 needs to be qualified by DVD:: And I don't see Print as a member function in your class.

1
2
3
4
5
//  In the public section of the class header: 
void Print (ostream & os) const;

//  Line 61
void DVD::Print (ostream & os) const

With those changes, it compiles clean for me.





You are correct, Anon. I did that too (as you were typing) and got the same results. Although I didn't use the "const" parts. What do they do?


Thank you for your help and putting up with silly questions. I feel like I actually learned as I did this, not just "got the answers" and copied and pasted. It made me think it through logically.

One last question, I can't seem to figure out how to implement a '\n' properly after the
"press any key to continue" between each array.

My output is properly formatted, but its just 100 straight lines (after each array is done), I was hoping to split each array after the "press any key to continue" if possible.
Last edited on
I'm not sure what you're trying to do.

To me, line 82: system ("pause'); is in the wrong place. It should be at line 84. This assumes you only want to pause at the end of the program before returning to the operating system.

If you really intended to pause between each DVD, then leave the call at line 82. Simply add:
1
2
  //  After line 82
  cout << endl;

Last night after we had talked, system ("pause") wouldn't compile. I did some research and found that I had a header missing that enabled it for my compiler.

I likely put it in the wrong place by accident.

For cleanness:
I'm trying to just put an extra line between each array (after the output line "press any key to continue").

Although if the entire array printed all at once, that would work too. This would be addressed by moving the system("pause") as you mention. I'll try both your options and see which looks better.

Thank you again!

Edit: That did it! Thanks again.

(I had tried cout but was using '\n' instead of endl which was why I couldn't get it to work)
Last edited on
Topic archived. No new replies allowed.