Goals for This Project: Break tasks down into functions Master pass by reference
ID: 3878917 • Letter: G
Question
Goals for This Project:
Break tasks down into functions
Master pass by reference or pass by value
Use character arrays and
Use struct to model composite data type
Use array of objects to model a collection
File input/output
Problem You Need to Solve for This Lab:
You are asked to write an app to keep track of a relatively small music library. The app should load song information from a data file once the app is started. It should allow user to view, add, remove, and search for songs. The app should save the data back to the same data file when the program exits.
What Your Program Should Do:
Write an interactive text based menu interface (using a loop) that will allow the user to
Enter information for a new song
Display information for all the songs in the database with index for each song
Remove a song by index
Search for songs by a certain artist
Search for songs by a certain album
Quit For each song, you need to keep track of:
title
artist
duration
album
Allow the program to keep looping until user wants to quit. When the program starts, it should load the tasks from external file ("songs.txt") into memory. When user enters information about the new song, the program needs to read them in, save them in memory and eventually write them to the external data file ("songs.txt"). The file format could look like:
Stereo Hearts;Gym Class Heroes;3;34;The Papercut Chronicles II
Counting Stars;OneRepulic;4;17;Native
The ';' is used as a delimiter or field separator. Each record ends with a new line character. Also the above sample data came from my teen son, not a reflection of your instructor’s music taste
Some Implementation Requirements:
1. Write at least four functions WITH arguments for this assignment.
2. Use struct named Song to model each song
3. Use array of structs to model the collection of songs.
4. Hint: In this assignment, some data fields may have multiple words in it. Therefore, you now SHOULD read using the 3 argument version of get.
5. Watch out. When using the 3 argument version of get you need to make sure to remove the delimiter or newline. Therefore, anytime you read (even a confirmation message), make sure to eat the newline using cin.ignore(...)!
6. Make sure to have a delimiter written between each item in the file – like a newline. This will be important when you read the information back from the file.
7. For submission, your data file should contain a sufficient set of test data. It should have test cases for same artist with multiple songs and same album with multiple songs in it.
Explanation / Answer
main.cpp
----------------------------------------------------
#include <fstream>
#include <iostream>
#include "DB.h"
DB::DB(){
this->currSize = 0;
}
DB::~DB(){
delete [] songs;
};
const unsigned int DB::getCurrSize() const {
return this->currSize;
}
/*
void DB::growDB(){
if (currSize > 2 * this->jumpAmount - 1){
Song tmp = new Song[this->currSize + this->jumpAmount];
for (int i = 0; i < currSize; i++){
Song s = this->get(i);
tmp[i] = s;
delete s;
}
delete [] this->songs;
this->songs = new Song[this->currSize + this->jumpAmount];
}
}
*/
void DB::add(Song s){
if (currSize < this->maxSize){
this->songs[currSize] = s;
this->currSize++;
}
}
void DB::add(Song s, unsigned int index){
if ((currSize < this->maxSize) && (index < this->maxSize)){
this->songs[index] = s;
this->currSize++;
}
}
Song DB::get(unsigned int index){
return songs[index];
//return this->songs[index];
}
void DB::remove(unsigned int index){
if ((this->currSize > 0) && (this->currSize < maxSize) && (index <= this->currSize) && (index > 0)){
for (int i = 0; i < currSize; i++){
this->songs[i] = this->songs[i+1];
}
this->currSize--;
}
}
bool DB::save(char filename[1024]){
std::ofstream file(filename);
bool success = false;
if (file){
for (int i = 0; i < currSize; i++){
file << songs[i].getTitle() << ";"
<< songs[i].getArtist() << ";"
<< songs[i].getLengthMinutes() << ";"
<< songs[i].getLengthSeconds() << ";"
<< songs[i].getAlbum() << std::endl;
}
success = true;
}
file.close();
return success;
}
//count the number of lines in teh file
//set the size of the db to this + 1
bool DB::loadData(const char filename[]){
bool success = false;
struct Loader {
char title[128];
char artist[128];
char album[128];
char length_minutes[128];
char length_seconds[128];
};
Loader l;
std::ifstream in(filename);
int i = 0;
char dummyLine[1024];
while (in.getline(dummyLine,1024)){
++i;
}
delete [] this->songs;
this->songs = new Song[i + (this->jumpAmount) ];
in.close();
in.open(filename);
int count = 0;
while ( in.getline(l.title,Song::MAX_CHAR,';')
&& in.getline(l.artist,Song::MAX_CHAR,';')
&& in.getline(l.length_minutes,Song::MAX_CHAR,';')
&& in.getline(l.length_seconds,Song::MAX_CHAR,';')
&& in.getline(l.album,Song::MAX_CHAR,' ')
){
Song s(l.title,l.artist,l.album,atoi(l.length_minutes),atoi(l.length_seconds));
this->add(s);
/*
Song *s = new Song(l.title,l.artist,l.album,atoi(l.length_minutes),atoi(l.length_seconds));
this->songs[count] = s;
*/
success = true;
count++;
}
in.close();
return success;
}
-------------------------------------------------------------------------------------------------------
DB.cpp
---------------------------
#include <fstream>
#include <iostream>
#include "DB.h"
DB::DB(){
this->currSize = 0;
}
DB::~DB(){
delete [] songs;
};
const unsigned int DB::getCurrSize() const {
return this->currSize;
}
/*
void DB::growDB(){
if (currSize > 2 * this->jumpAmount - 1){
Song tmp = new Song[this->currSize + this->jumpAmount];
for (int i = 0; i < currSize; i++){
Song s = this->get(i);
tmp[i] = s;
delete s;
}
delete [] this->songs;
this->songs = new Song[this->currSize + this->jumpAmount];
}
}
*/
void DB::add(Song s){
if (currSize < this->maxSize){
this->songs[currSize] = s;
this->currSize++;
}
}
void DB::add(Song s, unsigned int index){
if ((currSize < this->maxSize) && (index < this->maxSize)){
this->songs[index] = s;
this->currSize++;
}
}
Song DB::get(unsigned int index){
return songs[index];
//return this->songs[index];
}
void DB::remove(unsigned int index){
if ((this->currSize > 0) && (this->currSize < maxSize) && (index <= this->currSize) && (index > 0)){
for (int i = 0; i < currSize; i++){
this->songs[i] = this->songs[i+1];
}
this->currSize--;
}
}
bool DB::save(char filename[1024]){
std::ofstream file(filename);
bool success = false;
if (file){
for (int i = 0; i < currSize; i++){
file << songs[i].getTitle() << ";"
<< songs[i].getArtist() << ";"
<< songs[i].getLengthMinutes() << ";"
<< songs[i].getLengthSeconds() << ";"
<< songs[i].getAlbum() << std::endl;
}
success = true;
}
file.close();
return success;
}
//count the number of lines in teh file
//set the size of the db to this + 1
bool DB::loadData(const char filename[]){
bool success = false;
struct Loader {
char title[128];
char artist[128];
char album[128];
char length_minutes[128];
char length_seconds[128];
};
Loader l;
std::ifstream in(filename);
int i = 0;
char dummyLine[1024];
while (in.getline(dummyLine,1024)){
++i;
}
delete [] this->songs;
this->songs = new Song[i + (this->jumpAmount) ];
in.close();
in.open(filename);
int count = 0;
while ( in.getline(l.title,Song::MAX_CHAR,';')
&& in.getline(l.artist,Song::MAX_CHAR,';')
&& in.getline(l.length_minutes,Song::MAX_CHAR,';')
&& in.getline(l.length_seconds,Song::MAX_CHAR,';')
&& in.getline(l.album,Song::MAX_CHAR,' ')
){
Song s(l.title,l.artist,l.album,atoi(l.length_minutes),atoi(l.length_seconds));
this->add(s);
/*
Song *s = new Song(l.title,l.artist,l.album,atoi(l.length_minutes),atoi(l.length_seconds));
this->songs[count] = s;
*/
success = true;
count++;
}
in.close();
return success;
}
----------------------------------------------------------------
DB.h
--------------------------
#include "Song.h"
#include <cstdlib>
class DB {
public:
static const unsigned int maxSize = 1024;
DB();
~DB();
//getters
void add(Song s);
void add(Song s, unsigned int index);
Song get(unsigned int index);
const unsigned int getCurrSize() const;
const void showAll() const;
//setters
void remove(unsigned int index);
bool save(char filename[1024]);//made large to accomodate a long path
bool loadData(const char filename[]);
private:
//Song songs[DB::maxSize];
Song *songs = new Song[maxSize];
int currSize;
const unsigned int jumpAmount = 100;
//void growDB();//if currSize is 90% of jump amount, grow
//int computeInitialSize
};
/*
ifstream f("songs.txt");
int i = 0;
char dummyLine[5];
while (f.getline(dummyLine,5)){
++i;
}
*/
----------------------------------------------------------------------------------------
Song.cpp
---------------------------------
#include "Song.h"
#include <iostream>//can remove when not debugging
#include <cstring>
//set some initial dummy variables
Song::Song(){
strncpy(_title,"title",Song::MAX_CHAR);
strncpy(_artist,"artist",Song::MAX_CHAR);
strncpy(_album,"album",Song::MAX_CHAR);
_length_minutes = 0;
_length_seconds = 0;
}
/*
Song::Song(char* title, char* artist, char* album, int minutes, int seconds){
strncpy(_title,title,Song::MAX_CHAR);
strncpy(_artist,artist,Song::MAX_CHAR);
strncpy(_album,album,Song::MAX_CHAR);
_length_minutes = minutes;
_length_seconds = seconds;
}
*/
Song::Song(char title[], char* artist, char* album, int minutes, int seconds){
strncpy(_title,title,Song::MAX_CHAR);
strncpy(_title,title,strlen(title));
strncpy(_artist,artist,Song::MAX_CHAR);
strncpy(_album,album,Song::MAX_CHAR);
_length_minutes = minutes;
_length_seconds = seconds;
}
Song::~Song(){
//delete _title;
}
//setters
void Song::setTitle(char* title){
strncpy(_title,title,Song::MAX_CHAR);
}
void Song::setArtist(char* artist){
strncpy(_artist,artist,Song::MAX_CHAR);
}
void Song::setAlbum(char* album){
strncpy(_album,album,Song::MAX_CHAR);
}
void Song::setLengthMinutes(int length_minutes){
_length_minutes = length_minutes;
}
void Song::setLengthSeconds(int length_seconds){
_length_seconds = length_seconds;
}
//getters
const char* Song::getTitle() const {
return _title;
}
const char* Song::getArtist() const {
return _artist;
}
const char* Song::getAlbum() const {
return _album;
}
int Song::getLengthMinutes() const {
return _length_minutes;
}
int Song::getLengthSeconds() const {
return _length_seconds;
}
void Song::print() const {
std::cout << this->getTitle()
<< " : "
<< this->getArtist()
<< " : "
<< this->getAlbum()
<< " : "
<< this->getLengthMinutes()
<< " : "
<< this->getLengthSeconds()
<< std::endl;
}
--------------------------------------------------------------------------------------------
Song.h
---------------------------------------
class Song {
public:
Song();
~Song();
Song(char* title, char* artist, char* album, int length_minutes, int length_seconds);
static const int MAX_CHAR = 1024;
//setters
void setTitle(char* title);
void setArtist(char* artist);
void setAlbum(char* album);
void setLengthMinutes(int length_minutes);
void setLengthSeconds(int length_seconds);
//getters
const char* getTitle() const;
const char* getArtist() const;
const char* getAlbum() const;
int getLengthMinutes() const;
int getLengthSeconds() const;
//For Debug only
void print() const;
private:
char _title[Song::MAX_CHAR];
char _artist[Song::MAX_CHAR];
char _album[Song::MAX_CHAR];
int _length_minutes;
int _length_seconds;
};
---------------------------------------------------------------------------------------
UI.cpp
--------------------------------------
#include "UI.h"
#include <iomanip>
#include <iostream>
using namespace std;
void UI::displayChoices(){
cout << setw(80) << left << "Enter in a new song's info " << right << "(n)" << endl;
cout << setw(80) << left << "Show or display songs current loaded " << right << "(d)" << endl;
cout << setw(80) << left << "Delete or remove a song by a given index " << right << "(r)" << endl;
cout << setw(80) << left << "Search for songs by artist " << right << "(a)" << endl;
cout << setw(80) << left << "Search for songs by album " << right << "(b)" << endl;
cout << setw(80) << left << "Terminate or quit the program " << right << "(q)" << endl;
}
void UI::displaySongHeader(){
cout << setw(10) << left << "Index"
<< setw(30) << left << "Title"
<< setw(45) << left << "Artist"
<< setw(10) << left << "Duration"
<< setw(20) << left << "Album"
<< endl;
}
template <class T> void UI::get(T &var){
T val;
cin >> val;
while (!cin){
cout << "Data entered was invalid" << endl;
cin.clear();
cin.ignore(UI::MAX_CHAR,' ');
cin >> val;
}
cin.ignore(UI::MAX_CHAR,' ');
var = val;
}
void UI::get(char str[], unsigned int size){
cin.get(str, size, ' ');
while(!cin){
cout << "Field cannnot be empty. Please enter in something." << endl;
cin.clear();
cin.ignore(UI::MAX_CHAR,' ');
cin.get(str, size, ' ');
}
cin.ignore(UI::MAX_CHAR,' ');
}
--------------------------------------------------------------------------------------------
UI.h
-----------------------------
class UI {
public:
static const unsigned int MAX_CHAR = 1024;
void displaySongHeader();
void displayChoices();
template<class T> void showAll();
template <class T> void get(T &var);
void get(char str[], unsigned int size);
};
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.