Use getline and >> when read file C++

2019-01-29 08:54发布

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

Example of data file

David Beckham
80 90 100 20 50
Ronaldinho Gaucho
99 80 100 20 60
....

First of all, I have structure

struct Player {
string name;
int score[5];
} player[size]

When read data from file

int i = 0;
while(!file.eof())
    {
        for (int j = 0; j < 2; j++) //read each 2 two lines
        {               
            if(j==0) // name
            {               
                getline(file, player[i].name);  
            }
                        else if(j==1) // score
            {
                for(int k=0; k<5; k++) file >> player[i].grade[k];
            }
                }
         i++; //move to next player
    }

Problem is after read all scores (of first player), it seems like doesn't go to next line to continue read next name, kind of mess up there. So any suggestions to correct my code or new idea to do this?

标签: c++ getline
2条回答
劫难
2楼-- · 2019-01-29 09:03

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

查看更多
淡お忘
3楼-- · 2019-01-29 09:26

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));
}
查看更多
登录 后发表回答