![](/img/trans.png)
[英]C++ Reading from a file - First line of file being skipped, and last line being repeated twice
[英]C++ line being skipped while reading a file for no reason
假設我正在讀取一個文件(具體來說是“ infile.txt”)來計算測驗分數的平均值。 正在讀取的文件如下:
塞巴斯蒂安·傑克(Sebastian Jack)40 50 60 72 39 67 85 10 92 83
里克丹48 19 2 3 29 10 60 72 83 91
Yao耀48 30 68 27 94 81 20 38 90 81
甲板豪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
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.
}
我認為函數ignore(numeric_limits<streamsize>::max(), '\\n');
正在忽略,直到得到另一個'\\n'
,嘗試不使用它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.