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

标签 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 数字,这是每个学生的平均分数。但是,第二和第四名学生无故被跳过。这是输出:

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

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

最佳答案

Jaspreet 的建议是正确的;我会添加一些背景知识。

您的程序逻辑是读取数据的一种有效方式。 (另一种可能性,对于面向行的数据“记录”通常更直观,是逐行读取并单独解析每一行,这使得每个“记录”的结尾显而易见。)

现在为什么是ignore()跳过行?原因是空行在那一点已经被跳过了。详细说明:

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

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

如果数据可以想象地包含一个偶然的错误(比如,数字中的一个字母),换行符仍然可以用作可以尝试重新同步的地方,以实现健壮的编程。但在您的情况下,重新同步会自动发生(可能是在读取了一个解析错误的“记录”后,其名称中包含一个数字)。

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

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

以下是一些可能有效的片段。 playerT是保存数据“记录”的类。我实现了输入函数,因为清除输入流的失败位有点棘手。

/// 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 细节(如清除失败位等)以及计算平均值的“业务逻辑”已移至专用函数。

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

关于无故读取文件时跳过 C++ 行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41398280/

相关文章:

c++ - 如何在 C++ 中使用数组演示内存错误

c++ - 为什么我的链表代码会导致链接错误?

C++ 绑定(bind)非静态成员函数

c++ - 对八皇后中的回溯感到困惑

java - GIWS 在构建类时终止进程

c++ - 在 Windows 上以编程方式解压缩 AES 加密的 zip 文件

c++ - gmock 可以模拟重载方法吗?

c++ - 如何在 C++ 的单独文件中定义成员结构

c++ - std::optional<std::reference_wrapper<T>> - 可以吗?

c++ - 将单个数组复制到多维数组