0

I am trying to read into a nested struct from a txt file. The output keeps repeating the same nested output. I attempted to nest two for loop but it didn't even read at all, my display was a blank screen. So I was able to get the file to read now but it repeats the same title and yearPub information for all entries.

#include<iostream>
#include<string>
#include<fstream>
#include<iomanip>


using namespace std;


struct Discography {
    string title;
    int yearPub;
};


struct Collection {
    string name;
    string genres;
    int startYear;
    int endYear;
    Discography records;
};
void readFile(ifstream&, string, Collection [], int&);
const int DATA_FILE = 10;

int main() {
    
    ifstream inputMusic;
    Collection music[DATA_FILE];
    Discography records;    
    const int DISPLAY_ALL = 1,
        SEARCH_ARTIST_NAME = 2,
        SEARCH_GENRE = 3,
        SEARCH_TITLE = 4,
        SEARCH_YEAR = 5,
        QUIT_CHOICE = 6;
    int choice;
    int count = 0;
    int numFile = count;
    string nameArtist,results;
    
    readFile(inputMusic, "My_Artists.txt", music, count);
void readFile(ifstream& inputMusic, string data, Collection music[], int &count)
{
    inputMusic.open(data);
    if (!inputMusic) 
    {
        cout << "Error in opening file\n";
        exit(1);
    }
    else 
    {
        while (!inputMusic.eof()) 
        {
            inputMusic >> music[count].name
                >> music[count].genres
                >> music[count].startYear
                >> music[count].endYear
                >> music[count].records.title
                >> music[count].records.yearPub;
            count++;
        }
        inputMusic.close();
    }
    return;
};
InputFile:
MJ
Pop
1980
2020
BAD 1990
DRE
Rap
1970
2022
CRONIC 1995
EMINEM
Rap
1998
2022
ENCORE 2002
WHITNEY
R&B
1974
2008
SOMEBODY 1987


OUTPUT:
Name : MJ
Genre: Pop
Start Year: 1980
End Year: 2020
Title: BAD  Year Published: 1990
----------------------------------------------------
Name : DRE
Genre: Rap
Start Year: 1970
End Year: 2022
Title: BAD  Year Published: 1990
----------------------------------------------------
Name : EMINEM
Genre: Rap
Start Year: 1998
End Year: 2022
Title: BAD  Year Published: 1990
----------------------------------------------------
Name : WHITNEY
Genre: R&B
Start Year: 1974
End Year: 2008
Title: BAD  Year Published: 1990
----------------------------------------------------
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
Ronin
  • 5
  • 2
  • 1
    See: https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong. – wohlstad Dec 10 '22 at 15:21
  • Don't forget that if your output is wrong, it maybe that your **printing** code that has the bug. Apart from the `eof` issue (see comment above) I don't see any great problem in the posted code. – john Dec 10 '22 at 15:34
  • Please show your input file and the output you observe. – n. m. could be an AI Dec 10 '22 at 16:24
  • sorry for the big text, not sure how to add additional information..but I add the additional files...if you notice the bad title repeats on all entries... – Ronin Dec 10 '22 at 21:42
  • You only show a part of your `main` function, it cannot be checked. Please post a [mcve]. – n. m. could be an AI Dec 10 '22 at 21:50

2 Answers2

0

I would suggest going in a little different direction to make sure you don't write a lot of unneeded code. Here's the exact same example withe a few improvements. First improvement is the way you store your data. Instead of storing it in an array with seperate size variables and more arguments and that why not use vector. Next thing is the way you input data into your program. Not very clear. Better way would be saving it in csv file. Here's the code:

#include<iostream>
#include<string>
#include<fstream>
#include<iomanip>
#include<vector>

using namespace std;


struct Discography {
    string title;
    int yearPub;

    Discography(string name, int publish) : title(name), yearPub(publish) {}
    Discography() {}
};


struct Collection {
    string name;
    string genres;
    int startYear;
    int endYear;
    Discography records;

    Collection(string artistName, string genre, int start, int end, string title, int yearPublished)
        : name(artistName), genres(genre), startYear(start), endYear(end), records(title, yearPublished) {}
    Collection() {}
};

void readFile(vector<Collection> &music)
{
    ifstream file;
    file.open("myArtists.txt");
    if (file.is_open())
    {
        string line;
        while (getline(file,line))
        {
            int pos;
            pos = line.find(';');
            string name = line.substr(0, pos);
            line = line.substr(pos+1);

            pos = line.find(';');
            string genre = line.substr(0, pos);
            line = line.substr(pos+1);

            pos = line.find(';');
            int start = stoi(line.substr(0, pos));
            line = line.substr(pos+1);

            pos = line.find(';');
            int end = stoi(line.substr(0, pos));
            line = line.substr(pos+1);

            pos = line.find(';');
            string title = line.substr(0, pos);

            int publish = stoi(line.substr(pos+1));

            music.emplace_back(Collection(name,genre,start,end,title,publish));

        }
    }
    else
    {
        cout << "Error in opening file" << endl;
    }
    file.close();
    return;
};
void print(vector<Collection> &music)
{
    for(int i = 0; i < music.size(); i++)
    {
        cout << "Name: " << music[i].name << endl
             << "Genre: " << music[i].genres << endl
             << "Start Year: " << music[i].startYear << endl
             << "End Year: " << music[i].endYear << endl
             << "Title: " << music[i].records.title << "  Year Publiszed: " << music[i].records.yearPub << endl
             << "----------------------------------------------------" << endl << endl;
    }
}



int main() {

    vector<Collection> music;
    readFile(music);
    print(music);
}

There are constructors for the structs enabling quick and easy one line creation of the objects. This coupled with vectors' dynamic size and implemented add function emplace back makes it really easy to add to the list. Now the only thing left is to gather the data from the file. To make it more clear its always best to use a csv (comma seperated value) type file where each line is it's own object/item in your list and each variable is seperated in this instance with a colon. You go through the entire file with the while(getline(file, line)) loop seperating the values from the colons in an easy implemented string function find(character) which returns you the position of the colons in that single line of text from your file. After you find and seperate your data, make sure your numbers are numbers and strings are string. you can switch string to number via stoi function (String TO Integer). All your data is stored in music Collection.

The Output:

output

Monogeon
  • 346
  • 1
  • 9
  • 1
    Thank you for the insight and input, my professor didnt fully explain about Vectors mainly focused on arrays and not even mentioned csv files, I will have to do more research on it on my own time and youtube examples. – Ronin Dec 11 '22 at 03:10
  • Slightly picky. I would not you don't use a CSV file (as they are seporated with ';' not ','. – Martin York Dec 26 '22 at 19:58
  • 1
    The next improvement I would make is not to use a `readFile()` function. As this limits you to files. But rather specify the input and output operators for the two classes `Discography` and `Collection`. That way you can simply read things from a stream. It also allows you to simply build the vector using interators. – Martin York Dec 26 '22 at 20:00
  • Probably best to use text rather than inbed an image into the question. – Martin York Dec 26 '22 at 20:01
  • output as text might not fully explain the exact output you get. With an image you see exactly what i saw without me being a middleman explaining what happened – Monogeon Dec 26 '22 at 20:06
  • @MartinYork i agree with your other comment though. Limiting to files is not ideal. Using streams and other such would also be nice. Then again i doubt this project will go on to have that such improvements but who knows – Monogeon Dec 26 '22 at 20:08
0

Like @Monogeon I would use std::vector<> over an array for storage.

But I think the more idiomatic way to read and write objects in C++ is to define the input and output operators for a class. That way you can stream the objects to not only files but other stream like objects.

This then allows you to use further C++ idioms to manipulate the streams (like std::istream_iterator which reads objects using the input operator (i.e. operator>>).

I added the input and output operators below for you.

Then I create a facade FancyPrintCollection to make printing a collection object in a nice way for a human.

#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
#include <vector>
#include <iterator>

struct Discography {
    std::string title;
    int yearPub;

    friend std::ostream& operator<<(std::ostream& stream, Discography const& src)
    {
        return stream << src.title << " " << src.yearPub;
    }
    friend std::istream& operator>>(std::istream& stream, Discography& dst)
    {
        // The problem is that "title" could be multiple words.
        // We just know that we have a title followed by a number.
        // For V1 lets assume "title" is one word
        return stream >> dst.title >> dst.yearPub;
    }
};

struct Collection {
    std::string name;
    std::string genres;
    int startYear;
    int endYear;
    Discography records;

    friend std::ostream& operator<<(std::ostream& stream, Collection const& src)
    {
        return stream << src.name << "\n"
                      << src.genres << "\n"
                      << src.startYear << "\n"
                      << src.endYear << "\n"
                      << src.records << "\n";
    }
    friend std::istream& operator>>(std::istream& stream, Collection& dst)
    {
        std::getline(stream, dst.name);
        std::getline(stream, dst.genres);

        std::string numberLine;
        if (std::getline(stream, numberLine)) {
            dst.startYear = std::stoi(numberLine);
        }

        if (std::getline(stream, numberLine)) {
            dst.endYear = std::stoi(numberLine);
        }

        stream >> dst.records;
        stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        return stream;
    }
};

struct FancyPrintCollection
{
    Collection const& collection;
    public:
        FancyPrintCollection(Collection const& collection)
            : collection(collection)
        {}

    friend std::ostream& operator<<(std::ostream& stream, FancyPrintCollection const& src)
    {
        stream << "Name: " << src.collection.name << "\n"
               << "Genre: " << src.collection.genres << "\n"
               << "Start Year: " << src.collection.startYear << "\n"
               << "End Year: " << src.collection.endYear << "\n"
               << "Title: " << src.collection.records.title << "  Year Publiszed: " << src.collection.records.yearPub << "\n"
               << "----------------------------------------------------" << "\n" << "\n";
        return stream;
    }
};

int main()
{
    std::ifstream   inputMusic("Music.data");
    std::vector<Collection> music(std::istream_iterator<Collection>(inputMusic), std::istream_iterator<Collection>{});

    for (auto const& m: music) {
        std::cout << FancyPrintCollection(m);
    }
}
Martin York
  • 257,169
  • 86
  • 333
  • 562