/facepalm

Ok, so I have to make a program upon startup it has to read from a said file which looks something like this

Song Name
Artist
Genre
Track Time

That is one block of the array, their is a max of 10.
Ive been googling and searching the forums for help but nothing hit the nail on the head, or better yet my head.

If I can this function started I can possibly be well on my way to finishing.

Here is what I got so far...All I am doing is reading the data from the file and printing it out so see if it reads correctly

#include <iostream>
#include <fstream>
using namespace std; 

const int MAX_SONGS = 10;

int main()
{
    ifstream fin;
    int i =0, count = 0;
    
    struct music_list{
           string title, artist, genre;
           int track_time;
                      };
                      
    music_list songs[MAX_SONGS];
            
    fin.open("song.dat");
                                            
               getline(fin,songs[i].title);
               getline(fin,songs[i].artist);
               getline(fin,songs[i].genre);
               fin>>songs[i].track_time;
       
    while(fin.eof() && count<MAX_SONGS){
               getline(fin,songs[i].title);
               getline(fin,songs[i].artist);
               getline(fin,songs[i].genre);
               fin>>songs[i].track_time;
               count++;
               }
               
    for(i=0; i<=count; i++){
               cout<<songs[i].title
               <<songs[i].artist
               <<songs[i].genre
               <<songs[i].track_time;
               }
  
  
  fin.close();

  system("PAUSE");
  return 0;
}

I am sure there is a more efficient way of coding this simple function, I am just over thinking it.

In this course, we haven't touched vectors yet and we are suppose to use structs in this instance, I don't see whats confusing me, it seems like inside the while loop, count++ inside incrementing

while(fin.eof() && count<MAX_SONGS){
               getline(fin,songs[i].title);
               getline(fin,songs[i].artist);
               getline(fin,songs[i].genre);
               fin>>songs[i].track_time;
               count++;
               }

i never gets adjusted, so you will just keep overwriting the same song here.

Comments
Well said.

So how would I fix this?

Either adjust i or change the index variable to something that does change (i.e. count).

You also have the potential problem of mixing getline and >>. After you fix the index variable problem, make sure you're reading a full line when you use >>. Stick some debugging statements in there. You may need to have another getline statement in there to consume a stray '\n'.

And as always, when using eof (), be careful that you go through the loop the correct number of times. You may need to test for end-of-file more than once in that loop.

and that last bit mixing getline with >> is what is screwing me up, I do no how to address this problem efficiently, I dont see why fin>>
would not grab the whole integer

This seemed to work

while(!fin.eof() && i<MAX_SONGS){
                      getline(fin,song[i].title);
                      getline(fin,song[i].artist);
                      getline(fin,song[i].genre);
                      getline(fin,mystr);
                      stringstream(mystr)>>song[i].track_time;
                      i++;
                      }

and that last bit mixing getline with >> is what is screwing me up, I do no how to address this problem efficiently, I dont see why fin>>
would not grab the whole integer

Well, don't worry about it unless and until it happens, but I think it probably will happen. >> will grab the entire integer, but it won't grab the '\n' after the integer, so when you go to use getline later, you still have that '\n' that hasn't been read. getline will grab that remaining '\n' rather than the next line, so you may need to set up a dummy variable like this:

string dummyVariable; // info inthis variable will never be used
    while(fin.eof() && count<MAX_SONGS){
               getline(fin,songs[i].title);
               getline(fin,songs[i].artist);
               getline(fin,songs[i].genre);
               fin>>songs[i].track_time;
               getline (fin, dummyVariable);
               count++;
               }

Again, fix the i too. I'd have to see the exact file to see whether you need to do this, but I assume you do since it looks like the song titles start on a new line.

This link may be useful to read. It talks about formatted versus unformatted data and the danger of mixing the two.

http://www.daniweb.com/forums/thread90228.html

You have some other errors:
while(fin.eof() && count<MAX_SONGS)

It gets to the end of the file before this is true.
Need to change to:
while(!fin.eof() && count<MAX_SONGS)

To do multiple getline's without checking for EOF each time, you could do something like the following:

while(!fin.eof() && count<MAX_SONGS){
    if (getline(fin,songs[i].title))
    if (getline(fin,songs[i].artist))
                    //...and on, and on...
}

Although, it doesn't allow you to check for "empty" lines (lines with no data, only a newline). What if the file has an "empty" line? Try to think about things that a user could do to make your program not work (or return invalid input). If you're just starting to program, I wouldn't get too carried away with this, just do some simple things.

A way to check for an "empty" line might be to see if the length > 0:

string lineData;
while( !fin.eof() && recordCount<MAX_SONGS){
    getline(fin,lineData);

    //bypass empty lines
    if (lineData.length() > 0){
        //this line contains data, so it is ok to use
    }//if 
}//while

Another common thing that one might want to check (to avoid runtime errors), is if the file is open or not.

if (fin.is_open()){
    //we know that the file is open, so we can read from it
    
    string lineData;
    while( !fin.eof() && recordCount<MAX_SONGS){
        getline(fin,lineData);

        //eliminate empty lines
        if (lineData.length() > 0){
            //this line contains data, so it is ok to use

        }//if 
    }//while
}//if

So, now we check to see if the file is open and to see if the line contains data or not. What's next? Put the data into the right place.

If we only read one line of data per while loop iteration, it allows the while loop to check for EOF after each getline. And it also allows us to skip "empty" lines.

string lineData;
int recordCount = 0;
while( !fin.eof() && recordCount<MAX_SONGS){
    getline(fin,lineData);

    //eliminate empty lines
    if (lineData.length() > 0){
        //this line contains data, so it is ok to use
    }//if 
}//while

What about converting the string data that we read to an integer?
We can use the "atoi" function:

Given:

fin is type "ifstream"
lineData is type "string"

struct music_list{
string title;
string artist;
string genre;
int track_time;
};

songs is type "music_list"

getline(fin,lineData);
songs[recordCount].track_time = atoi(lineData.c_str());

The "atoi" function does stop reading if it encounters a non-numeric value though.
1234 will be converted to integer value 1234
12:34 will be converted to integer value 12

You said that the file follows the following format:
Song Name
Artist
Genre
Track Time

So, once we find a block of data, we know that the first line of data is "Song Name". The second line of data, is "Artist". etc... Which line numbers then have each of the following information?

Element Line #'s
Song Name 1, 5, 9, 13, 17...etc (1+4=5, 5+4=9, etc)
Artist 2, 6, 10, 14, 18 ...etc
Genre 3, 7, 11, 15, 19 ...etc
Track Time 4, 8, 12, 16, 20 ...etc

We can use if statements to check which line we have. But we only care about "valid" lines. Lines that contain data.
ex:

if (fin.is_open()){
    //we know that the file is open, so we can read from it
    
    string lineData;
    int recordCount = 0;
    int lineCount = 0;
    while( !fin.eof() && recordCount<MAX_SONGS){
        getline(fin,lineData);

        //eliminate empty lines
        if (lineData.length() > 0){
            //this line contains data, so it is ok to use

           if (lineCount == 0){
               //Song Name
               songs[recordCount].title = lineData;
           }//if
           else if (lineCount == 1){
              //Artist
           }//if
           else if (lineCount == 2){
              //Genre
           }//if
           else if (lineCount == 3){
              //Track Time
           }//if

           else if (lineCount == 4){
              //Song Name
           }//if
           else if (lineCount == 5){
              //Artist
           }//if
 
          // and on, and on...

            //only increment if length > 0; ie: if we have data
            lineCount ++;
        }//if 
    }//while
}//if

How can we generalize it so we don't have to write so many if/else if statements? We can use the "modulus" operator. It gives us the "remainder" after division.

0/4 = 0 with "remainder" = 0
1/4 = 0 with remainder = 1
2/4 = 0 with remainder = 2
3/4= 0 with remainder = 3

4/4 = 1 with "remainder" = 0
5/4 = 1 with remainder = 1
6/4 = 1 with remainder = 2
7/4= 1 with remainder = 3

8/4 = 2 with "remainder" = 0
9/4 = 2 with remainder = 1
10/4 = 2 with remainder = 2
11/4= 2 with remainder = 3

I think you get the point. Our struct has 4 variables.
lineCount / (number of struct variables)

We see a pattern for the remainder. In our example, it is always 0, 1, 2, or 3. So let's use this.

getline(fin,lineData);

//eliminate empty lines
if (lineData.length() > 0){
    //this line contains data, so it is ok to use
    if (lineCount % 4 == 0){
        //Song Name
        songs[recordCount].title = lineData;
    }//if
    else if (lineCount % 4 == 1){
        //Artist

    }//if
    else if (lineCount % 4 == 2){
    //Genre

    }//if
    else if (lineCount % 4 == 3){
    //Track Time
    songs[recordCount].track_time = atoi(lineData.c_str());

    //last element of the record, so increment the counter
    recordCount ++;
    }//if

    //increment the data lineCount
    //only increments when a line that contains data is found
    lineCount ++;
}//if

Alternately, we could use a "switch" and "case" statement.

switch (lineCount % 4)
{
    case 0:
        //Song Name
        songs[recordCount].title = lineData;
        break;
    case 1:
        //Artist
     
        break;
    case 2:
        //Genre
     
        break;
    case 3:
        //Track Time
        //convert string to integer value
        songs[recordCount].track_time = atoi(lineData.c_str());     
        break;
}//switch

That should be it.

Guess I forgot to increment "recordCount" in the "switch/case" version. It needs to be added to "case 3:"

switch (lineCount % 4)
{
    case 0:
        //Song Name
        songs[recordCount].title = lineData;
        break;
    case 1:
        //Artist
     
        break;
    case 2:
        //Genre
     
        break;
    case 3:
        //Track Time
        //convert string to integer value
        songs[recordCount].track_time = atoi(lineData.c_str());  

        recordCount ++;   
        break;
}//switch
This article has been dead for over six months. Start a new discussion instead.