Thread: Long question about Class and output

  1. #1
    Registered User
    Join Date
    May 2019
    Posts
    23

    Long question about Class and output

    Hello,

    I'm currently working on a program where I have a file I retrieve data from, and sort the different options (title, author, genre) and display the sorted list for each option. I have to make a header file for the class, and a main file that utilizes the header. I'm currently stuck because I am not seeing any output on my screen besides asking the user to enter a choice + entering a choice. The resulting sorted list does not show up but I'm not sure where in my header file I am making a mistake.

    Example of a line in the input file is
    "Title of book, author, genre"

    Code:
    #ifndef COLLECTION_H#define COLLECTION_H
    #include <iostream>
    #include <vector>
    #include <fstream>
    #include <sstream>
    #include <algorithm>
    
    
    using namespace std;
    
    
    class Collection
    {
        private:
            struct Book
            {
                string title;
                string author;
                string genre;
            };
            vector<Book> books;
    
    
        public:
    	
    	Collection()
    	{
    		ifstream inFile;
    		Book book;
    		string line;
    		
    		inFile.open("Books.txt");
    		
    		while(inFile.fail())
    		cout << "File not found.";
    		
    		while(inFile.eof())
    		{
    			getline(inFile, line);
    			stringstream stream(line);
    			getline(stream, book.title, ',');
    			getline(stream, book.author, ',');
    			getline(stream, book.genre, '\n');
    			books.push_back(book);
    		}				
    	}   
     
        void sortByTitle()
        {
            sort(books.begin(), books.end(), [](Book b1, Book b2) { return b1.title < b2.title; });
        }
        
        void sortByAuthor()
        {
            sort(books.begin(), books.end(), [](Book b1, Book b2) { return b1.author < b2.author; });
        }
        
        void sortByGenre()
        {
            sort(books.begin(), books.end(), [](Book b1, Book b2) { return b1.genre < b2.genre; });
        }
        
        void output()
        {
        	for (auto val:books )
        	{
        		cout << val.title << endl;
        		cout << val.author << endl;
        		cout << val.genre << endl;
    		}
    	}
    };
    
    
    #endif
    Code:
    #include <iostream>#include "Collection.h"
    using namespace std;
    
    
    int main() 
    {
    	Collection collect;
    	int choice;
    	
    	cout << "Please enter a number to sort the books by the following options:\n";
    	cout << "\tEnter 1 to sort by Title\n";
    	cout << "\tEnter 2 to sort by Author\n";
    	cout << "\tEnter 3 to sort by Genre\n";
    	cin >> choice;
    	
    	if (choice == 1)
    	{
    		collect.sortByTitle();
    	}
    	
    	if (choice == 2)
    	{
    		collect.sortByAuthor();
    	}
    	
    	if (choice == 3)
    	{
    		collect.sortByGenre();
    	}
    	
    	collect.output();
    }

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I would make the Collection::Book struct into a fully fledged Book class separate from Collection (which I might rename to BookCollection). This book class will handle its own I/O. You can then test reading a single book entry and printing its output, whereas now you have that all mashed together with Collection's I/O.

    I suggest that you overload operator<< for output and operator>> for input since you appear to have a canonical representation of a Book object.

    The BookCollection class can still store a vector of Book objects, but its I/O will use the Book I/O in a loop. Note that you should be using the getline call to control the input loop to read lines as strings, not eof().
    Last edited by laserlight; 07-18-2019 at 09:43 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    May 2019
    Posts
    23
    Quote Originally Posted by laserlight View Post
    I would make the Collection::Book struct into a fully fledged Book class separate from Collection (which I might rename to BookCollection). This book class will handle its own I/O. You can then test reading a single book entry and printing its output, whereas now you have that all mashed together with Collection's I/O.

    I suggest that you overload operator<< for output and operator>> for input since you appear to have a canonical representation of a Book object.

    The BookCollection class can still store a vector of Book objects, but its I/O will use the Book I/O in a loop. Note that you should be using the getline call to control the input loop to read lines as strings, not eof().
    Hi laserlight; unfortunately I am not allowed to make the book struct into a class. I have to have it as a struct under the Collection class as per guidelines of my assignment.

    I'm not sure what you mean by your last statement. I have the line of the input file into a stringstream so I can pull them out seperately. How would I use the getline as an input loop? I'm not too sure what you mean by that. I'm not sure I can do it any other way except what I have now as per rubric

    • A constructor that will read the file and populate the vector. Read the each line of the file the same way you did in Lab 1, making it a string stream, but now you will need to call getline on the stringstream repeatedly to get each part following the title. After populating a structure variable with the book info, push it into the vector. Make sure to test the the file was loaded successfully to avoid sorting an empty vector later.
    Last edited by underpressure; 07-18-2019 at 09:47 PM.

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Well, you can still define the I/O for the Book struct. A struct nested within a class is still a class, but within that outer class' scope and with the access defaults of a struct.

    For the Collection constructor, it should take an open ostream argument. If you want to open the file in the constructor, then what do you do if it fails? At the moment you're printing an error message (incorrectly in a loop), and then proceeding to read from the unopen file anyway. The right approach is to throw an exception, but the easier approach is to handle the opening of the file before creating the Collection object.

    How would I use the getline as an input loop?
    Code:
    while (getline(inFile, line))
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Registered User
    Join Date
    May 2019
    Posts
    23
    Quote Originally Posted by laserlight View Post
    Well, you can still define the I/O for the Book struct. A struct nested within a class is still a class, but within that outer class' scope and with the access defaults of a struct.

    For the Collection constructor, it should take an open ostream argument. If you want to open the file in the constructor, then what do you do if it fails? At the moment you're printing an error message (incorrectly in a loop), and then proceeding to read from the unopen file anyway. The right approach is to throw an exception, but the easier approach is to handle the opening of the file before creating the Collection object.


    Code:
    while (getline(inFile, line))
    Thanks laserlight. Using a while loop for getline works much better and cleaner than the other approach I was using. It is a shame that I am being taught bad code in my C++ class when there is much more efficient ways of doing it.

    This essentially fixed my whole problem. Thanks again!

  6. #6
    Registered User
    Join Date
    May 2019
    Posts
    23
    Soooo I spoke too soon. For some reason I'm getting some weird output when I run my program. Mainly theres seems to be random duplicates appearing and I'm not too sure why.
    My current code:
    Code:
    #ifndef COLLECTION_H
    #define COLLECTION_H
    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <sstream>
    #include <algorithm>
    #include <string>
    #include <iomanip>
    
    
    using namespace std;
    
    
    class Collection
    {
        private:
            struct Book
            {
                string title;
                string author;
                string genre;
            };
            vector<Book> books;
    
    
        public:   
     	
     		Collection()
     		{
     			Book book;
     			ifstream inFile;
     			string filename, text;
     			
     			inFile.open("Books.txt");
     			if (inFile.fail())
     			{
     				cout << "File not found. Please enter file name: ";
     				cin >> filename;
     				inFile.open(filename);
    			}
    			
    			while (getline(inFile,text))
    			{
    				stringstream lineStream(text);
    				getline(lineStream, book.title, ',');
    				getline(lineStream, book.author, ',');
    				getline(lineStream, book.genre);
    				books.push_back(book);					
    			}
    
    
    			inFile.close();
    		}
    
    
       	void sortByTitle()
        	{
            	sort(books.begin(), books.end(), [](Book b1, Book b2) { return b1.title < b2.title; });
        	}
        	
       	void sortByAuthor()
        	{
            	sort(books.begin(), books.end(), [](Book b1, Book b2) { return b1.author < b2.author; });
        	}
        	
        	void sortByGenre()
        	{
            	sort(books.begin(), books.end(), [](Book b1, Book b2) { return b1.genre < b2.genre; });
        	}
        	
        	void output()
        	{
        		cout << left << setw(41) << "Title" << left << setw(25) << "Author" << setw(20) << "Genre\n";
    
    
        		for (auto val:books)
        		{
        			cout << left << setw(40) << val.title << left << setw(25) << val.author << setw(20) << val.genre << endl;
    			}
    		}
    };
    #endif
    This is the output I get when I put in sort by title. It sorts the titles correctly however there are a bunch of duplicate outputs prior to the correct sorted file. It is different when sorting by author & genre but the error in code must be the same for all sorting options.

    Code:
    There are 3 fields for each book: title, author and genre.
    Please select an option to sort the books by a particular field by entering the correct number.
    1 = Sort by title
    2 = Sort by author
    3 = Sort by genre
    Please enter a sorting option: 1
    Title                                    Author                   Genre
                                                           Tony Gaddis              technical
                                             Liu Cixin                science fiction
                                             Kenzo Kitakana           fiction
                                             Gabriel Garcia Marquez   fiction
                                             Carol Dweck              psychology
                                             Ken Follet               historical fiction
                                             Ken Follett              historical fiction
                                             Bjarne Stroustrup        technical
                                             Elmarsi & Navathe        technical
    Ashes                                    Kenzo Kitakana           fiction
    Fall of Giants                           Ken Follet               historical fiction
    Fundamentals of Database Systems         Elmarsi & Navathe        technical
    Mindset: The New Psychology of Success   Carol Dweck              psychology
    One Hundred Years of Solitude            Gabriel Garcia Marquez   fiction
    Replay                                   Ken Grimwood             fantasy
    Starting out with c++                    Tony Gaddis              technical
    The C++ Programming Language             Bjarne Stroustrup        technical
    The Dark Forest                          Liu Cixin                science fiction
    The Pillars of the Earth                 Ken Follett              historical fiction
    Not exactly sure why this is happenig. There are no white spaces in my input file but it seems to be picking up random lines due to something?

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    What is your input file?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User
    Join Date
    May 2019
    Posts
    23
    This is simply it

    Long question about Class and output-books-jpg

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Do some debugging then: how many times does the loop that reads line by line run? For each iteration, what is the value of text?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    Registered User
    Join Date
    May 2019
    Posts
    23
    This is my train of thought

    Code:
    while (getline(inFile,text))
    Tells me that as long as the program can read a line of string it'll do the following

    Code:
     
    {
        stringstream lineStream(text);
        getline(lineStream, book.title, ',');
        getline(lineStream, book.author, ',');
        getline(lineStream, book.genre);
        books.push_back(book);                    
    }
    I associate each individual string object within text string which essentially allows me to get title, author, and genre. Then I push each of those string values into the vector books. This continues for as long as the program can perform getline from the input file, inFile. I have no clue where it is pulling the other garbage from. From my logical pov; it's behaving in a way I don't see how it is. I guess I'm missing the logic or my train of thought is incorrect

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Your logic sounds correct, but you're not doing sufficient error checking, e.g., you assume that the line is non-empty and that the getline calls succeed.

    Maybe it is a newline sequence problem, but either way, stop pontificating about your logic and debug your program. You have to know what's going on before you can proceed to find out if the error is with your logic or your implementation.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  13. #13
    Registered User
    Join Date
    May 2019
    Posts
    23
    Quote Originally Posted by laserlight View Post
    Your logic sounds correct, but you're not doing sufficient error checking, e.g., you assume that the line is non-empty and that the getline calls succeed.

    Maybe it is a newline sequence problem, but either way, stop pontificating about your logic and debug your program. You have to know what's going on before you can proceed to find out if the error is with your logic or your implementation.
    Makes sense. I guess for it is hard to debug my program when I can't think of potential errors. I am assuming there are no blank lines because looking at my text file there simply isn't any.

    I implemented a quick if condition to skip any blanks

    Code:
    			while (getline(inFile,text))
    			{
    				if (text == "")
    				continue;
    				stringstream lineStream(text);
    				getline(lineStream, book.title, ',');
    				getline(lineStream, book.author, ',');
    				getline(lineStream, book.genre);
    				books.push_back(book);					
    			}
    It has worked and all my duplicates I was seeing before are not showing up now. Now that it has been debugged essentially, I can't figure out what the problem was. Since I am essentially cheating by knowing what my input file looks exactly like. I don't see anywhere in the text file that has blank lines between my string lines that could have been causing my error. But obviously, the program is telling me different by adding in that command and that there were blank lines to skip. Is there just something inherent about my text file that I am not seeing visually?

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Are you programming on Windows? It looks like it from the image you posted.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Registered User
    Join Date
    May 2019
    Posts
    23
    Quote Originally Posted by laserlight View Post
    Are you programming on Windows? It looks like it from the image you posted.
    Yes I am programming in Windows 10 using Dev-C++

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 4
    Last Post: 04-02-2019, 04:10 AM
  2. Replies: 4
    Last Post: 04-06-2017, 05:08 PM
  3. Replies: 16
    Last Post: 06-07-2015, 03:28 PM
  4. Convert scanf output to long double and discard the last newline
    By uncreativename in forum C Programming
    Replies: 9
    Last Post: 04-18-2014, 10:58 PM
  5. Replies: 1
    Last Post: 10-11-2010, 01:53 AM

Tags for this Thread