Reputation: 61
I've been using Turbo C++ (:/) for sometime and came across a problem.
I am making a school project which needs file handling.My programme takes an input from the user and gives them 4 options:
CLASS DEFINITION:
#define max 20
class playlist{
static int ctr;
int sr;
char name[max];
class favsong
{
char song[max];
char genre[max];
char artist[max];
int rating;
public:
void playin()
{
cout<<"Enter song name"<<endl;
gets(song);
cout<<"Enter genre"<<endl;
gets(genre);
cout<<"Enter artist"<<endl;
gets(artist);
cout<<"Enter rating (out of 5)"<<endl;
cin>>rating;
}
void playout()
{
cout<<"\nSong:";
puts(song);
cout<<"Artist:";
puts(artist);
cout<<"Genre:";
puts(genre);
cout<<"rating:"<<rating<<endl;
}
favsong()
{
rating=0;
strcpy(song,"Not Assigned");
strcpy(genre,"Not Assigned");
strcpy(artist,"Not Assigned");
}
~favsong(){}
}favs[5];
public:
void input();
void output();
void showname()
{
puts(name);
}
int givesr()
{
return sr;
}
int existp (int n)
{
if(n==sr)
return 1;
else
return 0;
}
playlist()//constructor
{
strcpy(name,"Default playlist");
sr=ctr;
ctr++;
}
~playlist(){}
int existp(char arr[])
{
if(strcmp(name,arr)==0)
return 1;
else
return 0;
}
}wmp[max],obj;
int playlist::ctr=1;
void playlist::input()
{
cout<<"Enter playlist name"<<endl;
gets(name);
for(int i=0;i<5;i++)
favs[i].playin();
cout<<"Input complete"<<endl;
}
void playlist::output()
{
cout<<"Playlist no"<<sr;
for(int i=0;i<5;i++)
favs[i].playout();
cout<<"\noutput complete"<<endl;
}
Thing is I am running across a runtime error when I try to create a playlist: CODE:
case 2: cout<<"Creating new playlist...press enter to continue"<<endl;
getch();
obj.input();
h=0;
while(!file.eof()){
fileo.read((char*)&wmp[h],sizeof(wmp));
if(wmp[h].existp("Default playlist")){ //int existp(int*a) compares the object's
// name to the string"Default Playlist
pos=fileo.tellg();
file.seekp(-pos,ios::cur);
file.write((char*)&wmp,sizeof(wmp) );
}
h++;
}
break;
I have been on this for 3 days. It seemed pretty simple, but i don't know what the problem is here.
Also, the file keeps getting wiped after every run: CODE:
ofstream file("playlist.dat",ios::out|ios::app|ios::noreplace|ios::binary|ios::ate);
ifstream fileo("playlist.dat",ios::in|ios::binary);
-------------EDIT-----------
I took WhozCraig and qPCR4vir 's advice and seperate functions for the class members to insert themselves into a file. Thing is i ran into a roadblock again.. The output is showing ALL the data members whereas it is supposed to show only the sr no. and the playlist name. I think the input OR the output is wrong. CODE:
void existp() //function to show all existing playlists in the file.
{ ifstream tmp("playlist.dat",ios::in|ios::binary);
tmp.seekg(ios::beg);
tmp.read((char*)&wmp,sizeof(wmp));
for(int i=0;i<max;i++){
cout<<wmp[i].givesr();
wmp[i].showname();
}
tmp.close();
}
void fileput(){ //fileput and fileget are functions to put inner class member in //file
ofstream file("Playlist.dat",ios::out|ios::app|ios::nocreate|ios::binary);
file<<song<<endl<<artist<<endl<<genre<<endl<<rating<<endl;
file.close();
}
void fileget(){
ifstream file("Playlist.dat",ios::in|ios::binary);
file.getline(song,max,'\n');
file.getline(artist,max,'\n');
file.getline(genre,max,'\n');
file>>rating;
file.close();
}
//function definition of outer class function which puts outer class data members in file
void playput(long int pos){
ofstream file("Playlist.dat",ios::out|ios::app|ios::nocreate|ios::binary);
file.seekp(pos,ios::beg);
file<<name<<"\n";
for(int i=0;i<5;i++)
favs[i].fileput();
file.close();
}
void playget(long int pos){
ifstream file("Playlist.dat",ios::in|ios::binary);
file.seekg(pos,ios::beg);
file.getline(name,max,'\n');
for(int i=0;i<5;i++)
favs[i].fileget();
file.close();
}
-------------EDIT 2------------- FINALLY done! The input, output, deletion is going fine. Just one slight problem is that 'sr' is taking the memory values instead of the serial no. values from the file. So, instead of printing the second playlist as 2.Second Playlist, it's printing 2609.Second Playlist.
Thank You guys, All of you. I really appreciate it, you have seriously helped me A LOT! @WhozCraig and @qPCRvir:Thanks guys, for your answers and advice.I really appreciate it. :)
Upvotes: 1
Views: 844
Reputation: 3571
I dont want to touch the other answer, while now we have other problems and I cann mess all. I can not debbug, and this is not complet code, only a better way for me trying to explain the ideas.
#include <iostream>
#include <fstream>
using namespace std;
#define max 20
class playlist
{ static int ctr;
int sr;
char name[max];
class favsong
{ char song[max];
char genre[max];
char artist[max];
int rating;
public:
void playin()
{ cout<<"Enter song name"<<endl; gets(song);
cout<<"Enter genre"<<endl; gets(genre);
cout<<"Enter artist"<<endl; gets(artist);
cout<<"Enter rating (out of 5)"<<endl; cin>>rating;
}
void playout()
{ cout<<"\nSong:"; puts(song);
cout<<"Artist:"; puts(artist);
cout<<"Genre:"; puts(genre);
cout<<"rating:" <<rating <<endl;
}
void fileput(ofstream &file) // (Point 1:) put (get) inner class member (favsong) in file
{ // txt variant
file<<song<<endl<<artist<<endl<<genre<<endl<<rating<<endl;
// or the pure binary variant:
//file.write ((char*)this, sizeof(favsong));
}
void fileput() // put (get) inner class member (favsong) in file
{ ofstream file("Playlist.dat",ios::out|ios::app|ios::nocreate|ios::binary);
file<<song<<endl<<artist<<endl<<genre<<endl<<rating<<endl;
file.close();
}
void fileget(ifstream &file)
{ // txt variant
file.getline(song, max,'\n');
file.getline(artist,max,'\n');
file.getline(genre, max,'\n');
file>>rating;
}
void fileget()
{
ifstream file("Playlist.dat",ios::in|ios::binary);
file.getline(song, max,'\n');
file.getline(artist,max,'\n');
file.getline(genre, max,'\n');
file>>rating;
file.close();
}
favsong()
{ rating=0;
strcpy(song, "Not Assigned");
strcpy(genre, "Not Assigned");
strcpy(artist,"Not Assigned");
}
~favsong(){}
}favs[5];
public:
void input();
void output();
void playput(ofstream &file) // (Point 2:) puts outer class data members in file
{
// txt
file<<name<<"\n";
// or one "pure" binary variant
//file.write (name, sizeof(name));
for(int i=0;i<5;i++)
favs[i].fileput(file); // equal for both bin or txt
// or one pure binary variant to put the entery list at once:
//file.write ((char*)this, sizeof(playlist));
}
void playput( ofstream &file, long int pos) //puts outer class data members in file
{
file.seekp(pos,ios::beg);
playput(file); // equal for both bin or txt
}
void playput(long int pos) //puts outer class data members in file
{
ofstream file("Playlist.dat",ios::out|ios::app|ios::nocreate|ios::binary);
file.seekp(pos,ios::beg);
playput(file);
file.close();
}
// now write the equivalent for get.....
void playget(long int pos)
{
ifstream file("Playlist.dat",ios::in|ios::binary);
file.seekg(pos,ios::beg);
file.getline(name,max,'\n');
for(int i=0;i<5;i++)
favs[i].fileget(file);
file.close();
}
void showname() { puts(name); }
int givesr() { return sr; }
int existp (int n) { return (n==sr) ? 1 : 0; }
playlist()//constructor
{ strcpy(name,"Default playlist");
sr=ctr;
ctr++;
}
~playlist(){}
int existp(char arr[]) { return (strcmp(name,arr)==0) ? 1 : 0; }
}wmp[max],obj;
int playlist::ctr=1;
void playlist::input()
{ cout<<"Enter playlist name"<<endl;
gets(name);
for(int i=0;i<5;i++)
favs[i].playin();
cout<<"Input complete"<<endl;
}
void playlist::output()
{ cout<<"Playlist no"<<sr;
for(int i=0;i<5;i++)
favs[i].playout();
cout<<"\noutput complete"<<endl;
}
void existp() //function to show all existing playlists in the file.
{ ifstream tmp("playlist.dat",ios::in|ios::binary);
tmp.seekg(ios::beg);
tmp.read((char*)&wmp,sizeof(wmp));
for(int i=0;i<max;i++)
{ cout<<wmp[i].givesr();
wmp[i].showname();
}
tmp.close();
}
void save_playlist(const char *file_name) // (Point 3:)
{ ofstream file(file_name,ios::out|ios::binary);
for(int i=0;i<max;i++)
wmp[i].playput (file);
// or one pure binary variant to put all list at once:
//file.write((char*)&wmp,sizeof(wmp));
file.close();
}
void load_playlist(const char *file_name) // (Point 3:)
{ ifstream file(file_name,ios::in|ios::binary);
file.seekg(ios::beg);
for(int i=0;i<max;i++)
wmp[i].playget(file);
// or one pure binary variant to get all list at once:
//file.read((char*)&wmp,sizeof(wmp));
file.close();
}
Now, manipulate the lists....
void using_play_list (const char *file_name)
{
/*case 2:*/ cout<<"Creating new playlist...press enter to continue"<<endl;
getch();
obj.input();
load_playlist(file_name);
// now test and modify wmp as need.
int h=0;
while(h<max){
if(wmp[h].existp("Default playlist"))
{ //int existp(int*a) compares the object's
// name to the string"Default Playlist
// ????????????
}
h++;
}
save_playlist(file_name);
break;
}
This is all the idea from @WhozCraig , just to make the code simpler, not to solve yours final problems.
Upvotes: 0
Reputation: 3571
There are multiple issues with this code, to the point where you may consider standing back and rethinking what you're really trying to do.
For starters, the loop-read logic is fundamentally flawed in its item-read size. You should be reading one item per iteration, or just read the entire array once, then enumerate the array looking for an item available for a new playlist. At the core of the issue is this:
fileo.read((char*)&wmp[h],sizeof(wmp));
This reads an item up to the size of the entire wmp
array starting at the current file position, storing the results in the location in memory where the h
th element in the wmp
array is present (wmp+h
). I sincerely hope you see that if h
is ever larger than 0, this can potentially read past the end of the array and into undefined behavior.
If this were the only thing changed, it should be done as such:
fileo.read((char*)&wmp[h],sizeof(wmp[h]));
But this is just the tip of the iceberg. The loop itself is fundamentally flawed, in that you have a fixed fixed file size as well. There is no reason to have an iteration loop in reading the playlist array from disk (or writing to disk for that matter). Simply bulk-write the entire array such as
ofstream ofs(filename, ios::out|ios::binary|ios::trunc);
ofs.write((char*)&wmp, sizeof(wmp));
ofs.close();
and read it in with similar prejudice:
ifstream ifs(filename, ios::in|ios::binary)
ifs.read((char*)&wmp, sizeof(wmp));
ifs.close();
but honestly the correct way to do this is not the above. The correct approach to write your playlists to disk, for example, would be:
playlist::favorite
class that, given a binary stream object, can write itself to that output stream.playlist
class that, given a binary stream object, can write itself, as well as its current number of favorite songs, then each favorite song using the favorite-writer member function from (1) above.Similar logic would be used for reading the file.
playlist::favorite
that, given a input-binary stream object reference, can read itself from the stream.playlist
that, given an input-binary stream object reference, can read itself, as well as its favorite count, then for each favorite, invoke the favorite-reader from (1) above.Just one of many ways of doing this, but fairly logical, and nicely chained. Give it some thought. Even with this there are issues of platform portability, but unless you're sharing playlist files from one platform to the next that should not surface as an issue.
Upvotes: 2