Use getline and >> when read file C++

前端 未结 2 1615
隐瞒了意图╮
隐瞒了意图╮ 2020-12-22 07:27

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 >

相关标签:
2条回答
  • 2020-12-22 07:41

    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));
    }
    
    0 讨论(0)
  • 2020-12-22 08:04

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

    0 讨论(0)
提交回复
热议问题