Because data from file look like this: line 1 is name (first last), next line is score (score1 score 2 ....score5) and so on... So I think that I need getline for name and >
Your main problem is that you are reading the integers with >> directly from the stream. This combined with reading a string from the stream is a bad idea. Reading the strings removes the new line will reading with >> will not remove the new lines.
It is best not to mix the two forms. Either always use >> or always use getline(). Note: I am not saying best I am saying easiest. You can use them together when you understand the tradeoffs and how to compensate for the differences in their usage.
Thus it is easier to read the line of numbers into a string then parse the string.
std::string lineOfNumbers;
std::getline(file, lineOfNumbers);
// Now you have read all the numbers and the new line.
std::stringstream streamOfNumbers(lineOfNumbers);
while(streamOfNumbers >> value)
{
// Do something with number.
}
It is nearly always wrong to use:
while(!file.eof())
This is because the EOF flag is not set until you read past the eof. Note the last read will read upto but not past the eof. Thus you will enter the loop even though there is not data available.
The standard pattern is:
while(file >> object)
{
// Action
}
With this in mind I would define a class that represents all the information you want (ie two lines). A simple version would be
class TwoLineReader
{
public:
std::string line1;
std::string line2;
};
std::istream& operator>>(std::istream& stream, TowLineReader& record)
{
std::getline(stream, record.line1);
std::getline(stream, record.line2);
return stream;
}
TowLineReader obj;
while(file >> obj)
{
// Do stuff
}
This is fine if all you want to do is read lines.
But the data looks like it has a structure. So I would construct a class that represents the data and then read the data directly into that structure. So this is more what I would do. I would also replace the while() loops with algorithms.
Headers
#include <algorithm>
#include <iterator>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
/*
* Example Data
David Beckham
80 90 100 20 50
Ronaldinho Gaucho
99 80 100 20 60
*/
The Class:
class Player
{
std::string name;
std::vector<int> goals;
// Stream operator that reads a platers name and his goals.
friend std::istream& operator>>(std::istream& stream, Player& record)
{
// Read the name
std::getline(stream, record.name);
// Read the line of goals.
// Copies the data into goals.
std::string scores;
std::getline(stream, scores);
// std::copy replaces a while loop that pushes each number into the vector.
std::stringstream scorestream(scores);
std::copy( std::istream_iterator<int>(scorestream),
std::istream_iterator<int>(),
std::back_inserter(record.goals));
return stream;
}
};
Usage:
int main()
{
std::ifstream dataFile("data");
std::vector<Player> players;
// Copy all players into a vetor
std::copy( std::istream_iterator<Player>(dataFile),
std::istream_iterator<Player>(),
std::back_inserter(players));
}
After reading the last score, the line break is still sitting on the input buffer. You need to skip that. The ignore function is useful for that.
getline(file, player[i].name);
for (int k = 0; k < 5; ++k)
file >> player[i].grade[k];
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Check for errors as appropriate. The >>
operator, getline
, and ignore
all return the stream reference, which you can check for success or failure.
There's no need for that j
loop since each iteration does a completely different thing. Just write the j=0
case immediately followed by the j=1
case, and then get rid of the loop, like my code above. (And note that j
will never equal 2 inside the loop, so your condition was wrong anyway.)