无缘无故地读取文件时跳过C ++行

时间:2016-12-30 15:01:40

标签: c++

假设我正在阅读文件(" infile.txt"具体)以计算测验分数的平均值。正在阅读的文件如下:

  

Sebastian Jack 40 50 60 72 39 67 85 10 92 83

     

Lick Dan 48 19 2 3 29 10 60 72 83 91

     

Keng Yao 48 30 68 27 94 81 20 38 90 81

     

Deck Hao 91 82 65 55 79 93 89 19 23 37

输出到另一个文件是通过添加另一个int数字,这是每个学生的分数的平均值。但是,第2名和第4名学生被无缘无故地跳过了。这是输出:

  

Sebastian Jack 40 50 60 72 39 67 85 10 92 83 59.8

     

Keng Yao 48 30 68 27 94 81 20 38 90 81 57.7

这是我的代码:

// This is a program that will output the average quiz score of all student,
based on the student score file

#include <iostream>
#include <fstream>
#include <string>

int main()
{
    using namespace std;

    ifstream inf;
    ofstream outf;

    inf.open("infile.txt");
    outf.open("outfile.txt");

    string name;
    int score, total;
    double average;
    char next;
    while (inf >> name) {
        total = 0;

        outf << name << ' ';
        inf >> name;
        outf << name << ' ';

        while (inf >> score) {
            total += score;
            outf << score << ' ';
        }
        average = total / 10.0;
        outf << average << endl;

        inf.clear();
        inf.ignore(numeric_limits<streamsize>::max(), '\n');
    }

    inf.close();
    outf.close();
    return 0;
}

我的代码有错误吗?非常感谢!

2 个答案:

答案 0 :(得分:4)

Jaspreet的建议是正确的;我将添加一些背景信息。

您的程序逻辑是一种有效的读取数据的方法。 (另一种可能性,通常更直观的是面向行的&#34;记录和数据,是逐行读取并分别解析每一行,这使得每个&#34;记录的结束明显。)

现在为什么ignore()会跳过这些行?原因是此时已经跳过空行详细信息:

您的算法尝试读取数字,直到失败,因为下一个单词不是数字(或者因为达到了EOF)。现在,用于读取数字的库逻辑从跳过任何前导空格开始,包括换行符;你现在看到了问题。只有在读取下一个名字的第一个字母后,数字读取算法才会放弃并将读取的字母放回输入中。跳过的换行符不会被放回。然后是你的ignore并跳过我们站在开头的有效行。

好消息是,内部结构表示记录边界的记录(如此处:记录以数字序列结束;第一个非数字表示新记录的开头),您可以忽略任何空格,如换行符并且只是逐字解析。这使程序更加健壮:您可以处理没有空行或多个空行的数据,或者根本没有任何换行符!

如果数据可能会偶然包含偶然的错误(例如,数字中的字母),则线路中断仍然可以用作可以尝试重新同步的点,以进行稳健的编程。但在你的情况下,重新同步会自动发生(在阅读一个经过严格解析的&#34;记录&#34;可能有一个带有数字的名称之后)。

作为最后的讨论,我建议从stdin读取并使用这种类型的数据处理写入stdout;将数据源和目标留给调用者(通过myprog < infile.txt > outfile.txt等)。 Windows和* nix命令行都支持此功能。这使得程序更加通用,节省了编程工作。如果分配要求读取两个文件,则将数据上的实际算法工作分开(解析记录并计算平均值),从获取并写入数据,应该是一个只是得到一个istream和ostream的函数。这样就可以通过字符串流从任何来源提供数据,例如字符串。

实际上,可以通过定义与数据记录对应的类,并为其重载operator>>以及float record.averageScore()成员函数,将解析与算法工作分开: - )。这看起来更像是C ++。

以下是可能有效的几个片段。 playerT是一个持有&#34;记录&#34;的班级。数据的。我实现了输入函数,因为清除输入流的失败位有点棘手。

/// Currently just a first name, family name and vector of scores
class playerT
{
    string firstName;
    string lastName;
    vector<float> scores;
public:
    float averageScore() 
    { 
        return scores.size() 
                        ? accumulate(scores.begin(), scores.end(), 0.0)/scores.size() 
                        : 0; // avoid dividing by zero.
    }

    friend ostream & operator<<(ostream &os, const playerT &player);
    friend istream &operator>>(istream &is, playerT &player);
};

/// Produces output which could be read back with
/// the input operator. In particular, does not
/// output the average.
ostream &operator<<(ostream &os, const playerT &player) 
{
    //...
}

// Error handling is left to the caller.
// In particular, after EOF the player must be tested for completeness.
istream &operator>>(istream &is, playerT &player) 
{
    is >> player.firstName >> player.lastName; 

    player.scores.clear();
    float nextScore;
    while(is >> nextScore) 
    {
        player.scores.push_back(nextScore);
    }

    // Clear the expected format error indicating end of scores 
    // (and therefore this record), 
    // but not others which must be handled by the caller.
    is.clear(is.rdstate() & ~ios::failbit);
    return is;
}

主要功能归结为很少,这使信息流更清晰。原因是清除故障位等脏I / O细节被转移到专门的功能,以及&#34;业务逻辑&#34;计算平均值。

int main()
{
    do // loop to eof
    {
        playerT newPlayer;

        cin >> newPlayer;   
        // if this was the last record eof is now true
        // and the loop will exit after printing the last record.

        // todo: handle I/O errors
        cout << newPlayer << " (Av.: " << newPlayer.averageScore() << ')' << endl;
    }while(cin.good());

    return cin.bad(); // eof is ok. only bad is bad.
}

答案 1 :(得分:3)

我认为函数ignore(numeric_limits<streamsize>::max(), '\n');忽略了它,直到它得到另一个'\n',尝试不使用它。