How to read into Nested Structure C++ - c++

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
----------------------------------------------------

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:

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);
}
}

Related

Grab data from a specific struct by typing in name and year

I am building a wine inventory system for people who want to have an easy way of seeing the wine they own, which automatically gets its market price and county of creation.
this program should get a struct from multiple predefined structs seen in code below, by the user only typing name and year so it prints the entire struct, for example, I type in "Greenock creek Roennfelt road shiraz" and the year "2002" then it outputs the entire struct seen below.
I was thinking of using a read or get command but before researching how I should do make this I wanted to ask if there was a more efficient way of doing this.
the struct below is just one of many in a large list of predetermined structs in a second c++ file connected to the main file.
is this possible in c++ if so how would you recommend proceeding?
A struct in a different file:
struct red1 // one of the predetermined structs
{
string name = "Greenock Creek Roennfeldt Road Shiraz";
double year = 2002;
string place = "Australia";
double price = 295.00;
string type = "Redwine";
};
Main File input: (this part is not 100% yet it's just to show what I mean.
for (int i = 3; i < 10; i++)
{
string str; //input for data
cout << "Please enter the data of your Wine: " << endl;
cout << "Enter name: ";
getline(cin, wine.name);
cout << endl << "Enter year: ";
getline(cin, wine.year);
cout << endl;
cout << "your entered data: " << endl;
printwine(wine);
wineinventory.push_back(wine); // store in vector
}
I dont understand why you want to have several structs. I think you need just one and then create different instances for different wines. For the sake of the example I will use only year and name:
#include <vector>
#include <string>
#include <iostream>
struct wine {
int year;
std::string name;
};
// custom output operator to insert a wine into a ostream
std::ostream& operator<<(std::ostream& out, const wine& w) {
out << "year: " << w.year << " " << w.name;
return out;
};
int main() {
// fill a vector with different wines
std::vector<wine> wines { {2001,"the red one"}, {2005,"the white one"}};
// select a year
int year = 2001;
// pick the ones with matching year and print them
for (auto& w : wines) {
if (w.year == year) std::cout << w << "\n";
}
}
This will print:
year: 2001 the red one
There is already an accepted answer with many upvotes. Very good.
I just wanted to guide the new user in the correct direction. Since we are working with C++, we should use a more object oriented approach.
You have data and you have methods that should work on your data. For example, your Wine object has properties, the data members. And only the Wine object should operate on thes members. So I added / overloaded an inserter and extractor function. The inserter knows, how to print its data. Later, you would even encapsulate your data, and nobody but the functions should operate on it. If you later add one property to the Wine, you will adapt the inserter and the rest of the program will continue to work. You need to make this abstraction.
So, I recomend to learn the object oriented approach, otherwise, you will continue to wriet C code with some syntactic C++ sugar.
I drafted a skeleton example program for you. It should give you an idea of what I mean
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <iterator>
// A wine with its data and methods
struct Wine
{
std::string name{};
int year{};
std::string place{};
double price{};
std::string type{};
friend std::istream& operator >> (std::istream& is, Wine& w) {
return is >> w.name >> w.year >> w.place >> w.price >> w.type;
}
friend std::ostream& operator << (std::ostream& os, const Wine& w) {
return os << w.name << "\n" << w.year << "\n" << w.place << "\n" << w.price << "\n" << w.type << "\n";
}
};
// A Wine list (internally a vector) with its data and methods
class Wines
{
std::vector<Wine> wines{};
public:
void add(Wine& wine) { wines.push_back(wine); }
void remove(std::string wineName) { wines.erase(std::remove_if(wines.begin(), wines.end(), [&wineName](const Wine & w) { return w.name == wineName; }), wines.end()); }
bool findAndPrint(std::string& wineName, std::ostream& os) {
bool result = false;
std::vector<Wine>::iterator found = std::find_if(wines.begin(), wines.end(), [&wineName](const Wine & w) { return w.name == wineName; });
if (found != wines.end()) {
os << "\nWine found:\n" << *found;
result = true;
}
else
os << "\nNo wine with this name found\n";
return result;
}
friend std::istream& operator >> (std::istream& is, Wines& w) {
w.wines.clear();
std::copy(std::istream_iterator<Wine>(is), std::istream_iterator<Wine>(), std::back_inserter(w.wines));
return is;
}
friend std::ostream& operator << (std::ostream& os, const Wines& w) {
std::copy(w.wines.begin(), w.wines.end(), std::ostream_iterator<Wine>(os, "\n"));
return os;
}
};
int main(void)
{
// One wine
Wine wine;
// A lsit with wines
Wines wines;
// Add some data
std::cout << "\nEnter wine data. Name, Year, Place, Price, Type:\n";
std::cin >> wine;
wines.add(wine);
std::cout << "\n\nEnter another wine data. Name, Year, Place, Price, Type:\n";
std::cin >> wine;
wines.add(wine);
{
// Store all wines on disk
std::cout << "\nSaving on disk\n\n";
std::ofstream database("c:\\temp\\winelist.txt");
// Stores all wines in file
if (database) database << wines;
}
{
// Read back all wines from disk
std::cout << "\nReading from disk\n\n";
std::ifstream database("c:\\temp\\winelist.txt");
// Reads the complete list from file
if (database) database >> wines ;
}
// Search for a wine, if found, then remove it
std::cout << "\n\nWine List:" << wines << "\n\n\nSearch for a wine. Enter a wine name:\n" << wines;
std::string wineToSearch;
std::cin >> wineToSearch;
if (wines.findAndPrint(wineToSearch, std::cout)) {
wines.remove(wineToSearch);
std::cout << "\nRemoving wine from list: New List\n\n" << wines << "\n";
}
return 0;
}
There are of course tons of other possibilities. But you should get the idea.

C++ Data files, arrays, and calculations assignment

I'm new to C++ and I'm running into an issue on one of my assignments. The goal is to load data from a data file that looks like this.
item number date quantity cost per each
1000 6/1/2018 2 2.18
1001 6/2/2018 3 4.44
1002 6/3/2018 1 15.37
1001 6/4/2018 1 4.18
1003 6/5/2018 7 25.2
Basically I need to do calculations the average item number used for each date using arrays and do some other calculations with the cost. I'm getting really hung up with loading the data from the file and manipulating it for equations. This is what I have so far.
#include <cmath> //for math operations
#include <iostream> //for cout
#include <cstdlib> //for compatibility
#include <fstream>
#include <string>
using namespace std;
int main()
{
string date;
int EOQ, rp;
int count;
int itemnum[][];
double quantity[][];
double cost[][];
ifstream myfile;
string filename;
cout << "Data File: " << endl;
cin >> filename; // user enters filename
myfile.open(filename.c_str());
if(myfile.is_open())
{
cout << "file opened" << endl;
string head;
while(getline(myfile, head))
{
break; // so header won't interfere with data
}
while(!myfile.eof())
{ // do this until reaching the end of file
int x,y;
myfile >> itemnum[x][y] >> date >> quantity[x][y] >> cost[x][y];
cout << "The numbers are:" << endl;
for(count = 0; count < y; count++)
{
cout << itemnum[x][y] << endl;
break;
}
//cout << "Item: Reorder Point: EOQ: " << endl;
//cout << itemnum << " " << rp << " " << EOQ << endl;
break;
}
}
else
{
cout << "" << endl; //in case of user error
cerr << "FILE NOT FOUND" << endl;
}
cout << endl;
cout << "---------------------------------------------" << endl;
cout << " End of Assignment A8" << endl;
cout << "---------------------------------------------" << endl;
cout << endl;
system("pause");
return 0;
I haven't started working with the equations yet since I still can't get the file loaded in a simple array!!!
Thank you!
Link for data file : https://drive.google.com/file/d/1QtAC1bu518PEnk4rXyIXFZw3AYD6OBAv/view?usp=sharing
When working on these kinds of problems I like to break these down into the parts related to parsing. I'm using some of the standard libraries to do some of the work for me. I also created a couple of structures to help keep the information of the data organized. As for your date, I could of left that as a single std::string but I chose to break the date down into three individual types themselves and store them into a data structure just to show the capabilities of one of the functions that is involved with parsing.
What I prefer doing is to get either a single line of data from a file and save that to a string, or get the entire contents of a file and save that either to a large buffer or a vector of strings, unless if I'm handling specific type of code where that is not applicable such as parsing a wav file. Then close the file handle as I'm done reading from it! Then after I have all of the information I need, instead of trying to parse the file directly while it is opened I'd rather parse a string as it is easier to parse. Then after parsing the string we can populate our data types that we need.
I had to modify your data file slightly to accommodate for the extra white spaces so I saved your file as a text file with only a single white space between each data type within a single line of text. I also did not include the first line (header) information as I just omitted it completely. However this should still act as a guide of how to design a good work flow for an application that has good readability, reusability, try to keep it portable and as generic as possible. Now, what you have been waiting for; the demonstration of my version of your code:
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <exception>
struct Date {
int month;
int day;
int year;
Date() = default;
Date( int monthIn, int dayIn, int yearIn ) :
month( monthIn ),
day( dayIn ),
year( yearIn )
{}
};
struct DataSheetItem {
int itemNumber;
Date date;
int quantity;
double costPerEach;
DataSheetItem() = default;
DataSheetItem( int itemNumberIn, Date& dateIn, int quantityIn, double costPerEachIn ) :
itemNumber( itemNumberIn ),
date( dateIn ),
quantity( quantityIn ),
costPerEach( costPerEachIn )
{}
};
std::vector<std::string> splitString( const std::string& s, char delimiter ) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream( s );
while( std::getline( tokenStream, token, delimiter ) ) {
tokens.push_back( token );
}
return tokens;
}
void getDataFromFile( const char* filename, std::vector<std::string>& output ) {
std::ifstream file( filename );
if( !file ) {
std::stringstream stream;
stream << "failed to open file " << filename << '\n';
throw std::runtime_error( stream.str() );
}
std::string line;
while( std::getline( file, line ) ) {
if ( line.size() > 0 )
output.push_back( line );
}
file.close();
}
DataSheetItem parseDataSheet( std::string& line ) {
std::vector<std::string> tokens = splitString( line, ' ' ); // First parse with delimeter of a " "
int itemNumber = std::stoi( tokens[0] );
std::vector<std::string> dateInfo = splitString( tokens[1], '/' );
int month = std::stoi( dateInfo[0] );
int day = std::stoi( dateInfo[1] );
int year = std::stoi( dateInfo[2] );
Date date( month, day, year );
int quantity = std::stoi( tokens[2] );
double cost = std::stod( tokens[3] );
return DataSheetItem( itemNumber, date, quantity, cost );
}
void generateDataSheets( std::vector<std::string>& lines, std::vector<DataSheetItem>& dataSheets ) {
for( auto& l : lines ) {
dataSheets.push_back( parseDataSheet( l ) );
}
}
int main() {
try {
std::vector<std::string> fileConents;
getDataSheetItemsFromFile( "test.txt", fileContents );
std::vector<DataSheetItem> data;
generateDataSheets( fileConents, data );
// test to see if info is correct
for( auto& d : data ) {
std::cout << "Item #: " << d.itemNumber << " Date: "
<< d.date.month << "/" << d.date.day << "/" << d.date.year
<< " Quantity: " << d.quantity << " Cost: " << d.costPerEach << '\n';
}
} catch( const std::runtime_error& e ) {
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
NOTE This will not work with how your file currently is; this does not account for the first line of text (header information) and this does not account for any extra white spaces in between the data fields. If you add a single line of text when opening the file and read in a single line and just ignore it, then perform the loop to get all strings to add to vector to return back; your vectors will have the information in it but they will not be at the correct index locations of the vector because of all the extra white spaces. This is something you need to be aware of! Other than that; this is how I would basically design a program or application to parse data. This is by all means not 100% full proof and may not even be 100% bug free, but from a quick glance and running it through my debugger a few times it does appear to be without any noticeable bugs. There could also be some room for improvements for runtime efficiency, etc. but this is just a generalization of basic parsing.

Reading or writing binary file incorrectly

The output of the code show gibberish values for all the variables of the Student struct. When the display function is ran.
I've include the relevant code in each of the add and display function for the binary file.
For the second function, does the seekg pointer automatically move to read the the next record each time the for loop runs?
//Student struct
struct Student
{
char name [30];
float labTest;
float assignments;
float exam;
};
//Writing function
afile.open(fileName,ios::out|ios::binary);
Student S;
strcpy(S.name,"test");
S.labTest = rand()%100+1;
S.assignments = rand()%100+1;
S.exam = rand()%100+1;
afile.write(reinterpret_cast<char*>(&S),sizeof(S));
afile.close();
//Reading function
afile.open(fileName,ios::in|ios::binary);
afile.seekg(0,ios::end);
int nobyte = afile.tellg();
int recno = nobyte / sizeof(Student);
Student S;
//Loop and read every record
for(int i = 0;i<recno;i++)
{
afile.read(reinterpret_cast<char*>(&S),sizeof(S));
cout << "Name of Student: " << S.name << endl
<< "Lab mark: " << S.labTest << endl
<< "Assignment mark: " << S.assignments << endl
<< "Exam mark: " << S.exam << endl << endl;
}
afile.close();
There are a lot of problems with your code:
Calling your write function will permanently overwrite the last written data set. You have to add: ios::append, so that new data will be written behind the last data you wrote before.
After you move with afile.seekg(0,ios::end); to get with tellg the file size, you have to go back to the start of the file before reading with afile.seekg(0,ios::beg)
It looks that you use a char array to store a string. This is not c++ style! And it is dangerous how you use it. If you use strcpy, you can copy a string which is longer than the space you reserved for it. So you should prefer std::string for that. But you can't simply write a struct which constains std::string as binary! To get checked copy you can use strncpy, but that is still not c++ ;)
For the second function, does the seekg pointer automatically move to read the the next record each time the for loop runs?
Yes, the file position moves which each successful read and write.
A general remark writing binary data by simply dumping memory content:
That is not a good idea, because you can only read that data back, if you use the same machine type and the same compiler options. That means: A machine with different endianness will read data totally corrupted. Also a different integer type ( 32 bit vs 64 bit ) will break that code!
So you should invest some time how to serialize data in a portable way. There are a lot of libraries around which can be used to read/write also complex data types like std::string or container types.
A hint using SO:
Please provide code which everybody can simply cut and paste and compiled. I did not know what your Student struct is. So I take a lot of assumptions! Is your struct really using char[]? We don't know!
#include <iostream>
#include <fstream>
#include <cstring>
const char* fileName="x.bin";
struct Student
{
char name[100]; // not c++ style!
int labTest;
int assignments;
int exam;
};
// Writing function
void Write()
{
std::ofstream afile;
afile.open(fileName,std::ios::out|std::ios::binary|std::ios::app);
Student S;
strcpy(S.name,"test"); // should not be done this way!
S.labTest = rand()%100+1;
S.assignments = rand()%100+1;
S.exam = rand()%100+1;
afile.write(reinterpret_cast<char*>(&S),sizeof(S));
afile.close();
}
void Read()
{
//Reading function
std::ifstream afile;
afile.open(fileName,std::ios::in|std::ios::binary);
afile.seekg(0,std::ios::end);
int nobyte = afile.tellg();
int recno = nobyte / sizeof(Student);
afile.seekg(0, std::ios::beg);
Student S;
//Loop and read every record
for(int i = 0;i<recno;i++)
{
afile.read(reinterpret_cast<char*>(&S),sizeof(S));
std::cout << "Name of Student: " << S.name << std::endl
<< "Lab mark: " << S.labTest << std::endl
<< "Assignment mark: " << S.assignments << std::endl
<< "Exam mark: " << S.exam << std::endl << std::endl;
}
afile.close();
}
int main()
{
for ( int ii= 0; ii<10; ii++) Write();
Read();
}
EDIT. Apparently, I was a bit too late in responding. Klaus has compiled a better, more comprehensive response dwelling into other problems regarding C-style char [], std::string and the endianness of the platform.
You should append to the file opened for every record. In your code you don't have this, at all. Please write the code in a way we can copy and paste, and test. As a working example, you should write some code that can be compiled and run as below:
#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
// Student struct
struct Student {
char name[30];
float labTest;
float assignments;
float exam;
};
// Serializer
void serialize_student(const Student &s, const std::string &filename) {
// Append to the file, do not overwrite it
std::ofstream outfile(filename, std::ios::binary | std::ios::app);
if (outfile)
outfile.write(reinterpret_cast<const char *>(&s), sizeof(Student));
}
// Deserializer
std::vector<Student> deserialize_students(const std::string &filename) {
std::ifstream infile(filename, std::ios::binary);
std::vector<Student> students;
Student s;
while (infile.read(reinterpret_cast<char *>(&s), sizeof(Student)))
students.push_back(std::move(s));
return std::move(students);
}
int main(int argc, char *argv[]) {
// Generate records
std::vector<Student> mystudents;
std::generate_n(std::back_inserter(mystudents), 10, []() {
Student s;
std::strcpy(s.name, "test");
s.labTest = rand() % 100 + 1;
s.assignments = rand() % 100 + 1;
s.exam = rand() % 100 + 1;
return s;
});
// Print and write the records
for (const auto &student : mystudents) {
std::cout << student.name << ": [" << student.labTest << ','
<< student.assignments << ',' << student.exam << "].\n";
serialize_student(student, "students.bin");
}
// Read and print the records
auto records = deserialize_students("students.bin");
std::cout << "===\n";
for (const auto &student : records)
std::cout << student.name << ": [" << student.labTest << ','
<< student.assignments << ',' << student.exam << "].\n";
return 0;
}

How to create a class function that can uses other class functions with object variables as parameters

I am tasked to create a Print function that prints user inputted data that is specific to an object. This print function must use the Get() Function commands I created.
I have googled and looked for similar questions but could not find a way of how I could approach this. How can I create this function my teacher wants?
The object I want to print specifically is book1
My code:
#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
class Book {
public:
void SetTitle(string title_input);
string GetTitle();
void SetAuthor(string& author_input);
string GetAuthor();
void SetCopyRightYear(int copyright_year_input);
int GetCopyRightYear();
void PrintBook();
private:
string title;
string author;
int copyright_year;
};
void Book::SetTitle(string title_input) {
title = title_input;
}
string Book::GetTitle() {
return title;
}
void Book::SetAuthor(string& author_input) {
author = author_input;
}
string Book::GetAuthor() {
return author;
}
void Book::SetCopyRightYear(int copyright_year_input) {
copyright_year = copyright_year_input;
}
int Book::GetCopyRightYear() {
return copyright_year;
}
void Book::PrintBook() {
cout << "Title of Book: " << GetTitle() << endl;
cout << "Author of Book: " << GetAuthor() << endl; // Function is broken FIXME
cout << "Copyright Year: " << GetCopyRightYear() << endl;
}
int main ()
{
string title_input = "";
string author_input = "";
int copyright_year_input = 0;
Book book1;
Book book2;
Book book3;
Book book4;
cout << "Enter the book title: ";
cin >> title_input;
book1.SetTitle(title_input);
cout << book1.GetTitle();
cout << "Enter the author name: ";
cin >> author_input;
book1.SetAuthor(author_input);
cout << "Enter the copyright year: ";
cin >> copyright_year_input;
book1.SetCopyRightYear(copyright_year_input);
cout << PrintBook();
Book.h
#pragma once
#include <string>
class Book
{
public:
Book() = default;
~Book() = default;
const std::string GetTitle() const;
const std::string GetAuthor() const;
const int GetCopyRightYear() const;
void SetTitle(const std::string);
void SetAuthor(const std::string);
void SetCopyRightYear(const int);
void PrintBook();
private:
std::string title;
std::string author;
int copyright_year;
};
Book.cpp
#include "Book.h"
// ------------------------------
#include <iostream>
void Book::SetTitle(const std::string title_input)
{
title = title_input;
}
const std::string Book::GetTitle() const
{
return title;
}
const int Book::GetCopyRightYear() const
{
return copyright_year;
}
const std::string Book::GetAuthor() const
{
return author;
}
void Book::SetCopyRightYear(const int copyright_year_input)
{
copyright_year = copyright_year_input;
}
void Book::SetAuthor(const std::string author_input)
{
author = author_input;
}
void Book::PrintBook()
{
std::string output_str = "";
std::cout << "Title of Book: " << GetTitle() << std::endl;
std::cout << "Author of Book: " << GetAuthor() << std::endl;
std::cout << "Copyright Year: " << GetCopyRightYear() << std::endl;
}
main.cpp
// C++ Libraries.
#include <iostream>
#include <string>
// User classes
#include "Book.h"
// Namespaces
int main()
{
std::string title_input = "";
std::string author_input = "";
int copyright_year_input = 0;
// research dynamic memory allocation.
Book book1;
Book book2;
Book book3;
Book book4;
// user sets book title.
std::cout << "Enter the book title: ";
std::getline(std::cin, title_input);
book1.SetTitle(title_input);
// user sets the authors name
std::cout << "Enter the author name: ";
std::getline(std::cin, author_input);
book1.SetAuthor(author_input);
// user inputs the copyright year.
std::cout << "Enter the copyright year: ";
std::cin >> copyright_year_input;
book1.SetCopyRightYear(copyright_year_input);
// Display the information.
book1.PrintBook();
}
Notes:
When you start using multiple namespaces its easier to see what is what if you dont predefine them.
Const correctness means you and other developers know what can be changed and what cant. It also makes things clearer for the compiler.
std::getline reads the whole line including the blank spaces.
Just a quick note on clarity and understanding. At the moment your code is messy which makes it incredibly hard to debug not only for yourself but for others.
I can't tell on here but just in case, your classes should be in header and source code formatting, with a main source code file for the main function (entry point). Whether or not you've been told this information before I would highly recommend doing some research into basic C++. Just for starters I've put some links below to help. Once your code is neatly formatted you might work out what the problem is.
Happy coding :)
References:
Herb Sutter Cpp Convention 2014 - Simplicity over Complexity:
https://www.youtube.com/watch?v=xnqTKD8uD64
Headers and Includes - C++ formatting:
http://www.cplusplus.com/forum/articles/10627/
Also see the tutorials on cplusplus.com.

Reading from file separated with semicolons and storing into array

I am completely lost and have been trying for hours to read from a file named "movies.txt" and storing the info from it into arrays, because it has semicolons. Any help? Thanks.
movies.txt:
The Avengers ; 2012 ; 89 ; 623357910.79
Guardians of the Galaxy ; 2014 ; 96 ; 333130696.46
Code:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
struct Movie {
std::string name;
int year;
int rating;
double earnings;
};
int main()
{
const int MAX_SIZE = 100;
Movie movieList[MAX_SIZE];
std::string line;
int i = 0;
std::ifstream movieFile;
movieFile.open("movies.txt");
while (getline(movieFile, line, ';'))
{
movieFile >> movieList[i].name >> movieList[i].year >> movieList[i].rating >> movieList[i].earnings;
i++;
}
movieFile.close();
std::cout << movieList[0].name << " " << movieList[0].year << " " << movieList[0].rating << " " << movieList[0].earnings << std::endl;
std::cout << movieList[1].name << " " << movieList[1].year << " " << movieList[1].rating << " " << movieList[1].earnings << std::endl;
return 0;
}
What I want is to have:
movieList[0].name = "The Avengers";
movieList[0].year = 2012;
movieList[0].rating = 89;
movieList[0].earnings = 623357910.79;
movieList[1].name = "Guardians of the Galaxy";
movieList[1].year = 2014;
movieList[1].rating = 96;
movieList[1].earnings = 333130696.46;
I amended your code.
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
struct Movie {
std::string name;
int year;
int rating;
double earnings;
};
std::vector<std::string>
split(const std::string &s, char delim = ',')
{
std::vector<std::string> elems;
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim))
{
elems.push_back(item);
}
return elems;
}
int main()
{
std::vector<Movie> movieList;
std::string line;
std::ifstream movieFile;
movieFile.open("movies.txt");
while (getline(movieFile, line))
{
std::vector<std::string> columns = split(line,';');
Movie movie;
movie.name = columns[0];
movie.year = std::stoi(columns[1]);
movie.rating = std::stoi(columns[2]);
movie.earnings = std::stof(columns[3]);
movieList.push_back(movie);
}
movieFile.close();
for (const Movie & m: movieList)
{
std::cout << m.name << " " << m.year << " " << m.rating << " " << m.earnings << std::endl;
}
return 0;
}
Basicly, I added a split function that splits the lines using ';'. Also I use vector to store the movies rather than hard coded array of movies. Much better this way.
P.S. Second version without vectors
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
struct Movie {
std::string name;
int year;
int rating;
double earnings;
};
void split(const std::string &s, char delim, std::string elems[])
{
std::stringstream ss(s);
std::string item;
int i = 0;
while (std::getline(ss, item, delim))
{
elems[i++] = item;
}
}
int main()
{
//std::vector<Movie> movieList;
const int MAX_SIZE = 100;
Movie movieList[MAX_SIZE];
int movieNo = 0;
std::string line;
std::ifstream movieFile;
movieFile.open("/home/marcin/testing/movies.txt");
std::string columns[4];
while (getline(movieFile, line))
{
split(line,';', columns);
movieList[movieNo].name = columns[0];
movieList[movieNo].year = std::stoi(columns[1]);
movieList[movieNo].rating = std::stoi(columns[2]);
movieList[movieNo].earnings = std::stof(columns[3]);
++movieNo;
}
movieFile.close();
for (int i =0; i < movieNo; ++i) {
std::cout << movieList[i].name
<< " "
<< movieList[i].year
<< " "
<< movieList[i].rating
<< " "
<< movieList[i].earnings
<< std::endl;
}
return 0;
}
Use getline(my_movieFile, movie_name, ';') to get the name of the movie up to the ;.
You'll need to figure out how to remove the trailing whitespace from the name if necessary.. you can search for examples.
Read the rest of the line using getline(movieFile, line)
Use std::replace to replace all ; with a space in line
Put line into a std::stringstream.
Then extract the remaining fields from the stringstream using the >> operators.
Put this in loop do { ... } while (movieFile);
Also, don't hardcode an arbitrary number of movies. Use a std::vector<Movie> and push_back to add new ones.
I think you want to break your line into tokens using something like std::strtok. Check out the reference here. The example given on that page uses a blank as a separator, you would use a semicolon.