读取文件 C++ 时使用 getline 和 >>

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11039323/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:46:36  来源:igfitidea点击:

Use getline and >> when read file C++

c++getline

提问by Ronaldinho Learn Coding

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

因为文件中的数据看起来像这样:第 1 行是名称(倒数第一个),下一行是分数(score1 分数 2 ....score5)等等......所以我认为我需要 getline 作为 name 和 >> for分数

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?

问题是在读取了所有分数(第一个玩家的)之后,似乎没有转到下一行继续读取下一个名称,那里有点乱。那么有什么建议可以纠正我的代码或这样做的新想法吗?

回答by Rob Kennedy

After reading the last score, the line break is still sitting on the input buffer. You need to skip that. The ignorefunction is useful for that.

读完最后一个分数后,换行符仍然坐在输入缓冲区上。你需要跳过那个。该ignore功能对此很有用。

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 ignoreall return the stream reference, which you can check for success or failure.

根据需要检查错误。该>>运营商,getline以及ignore所有返回流的参考,你可以检查的成功或失败。



There's no need for that jloop since each iteration does a completely different thing. Just write the j=0case immediately followed by the j=1case, and then get rid of the loop, like my code above. (And note that jwill never equal 2 inside the loop, so your condition was wrong anyway.)

不需要那个j循环,因为每次迭代都做完全不同的事情。j=0直接写case后面跟着j=1case,然后去掉循环,就像我上面的代码一样。(请注意,j循环内永远不会等于 2,因此无论如何您的条件都是错误的。)

回答by Martin York

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.

最好不要混合两种形式。要么总是使用 >> 要么总是使用 getline()。注意:我不是说最好,我说的是最简单。当您了解权衡以及如何补偿它们的使用差异时,您可以将它们一起使用。

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.

这是因为直到您读过 eof 之后才会设置 EOF 标志。请注意,最后一次读取将读取到但不超过 eof。因此,即使没有可用数据,您也会进入循环。

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.

如果您只想读取行,这很好。
但是数据看起来像是有结构的。所以我会构造一个表示数据的类,然后将数据直接读入该结构。所以这更多的是我会做的。我还将用算法替换 while() 循环。

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));
}