假设我正在阅读文件(" 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;
}
我的代码有错误吗?非常感谢!
答案 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'
,尝试不使用它。