[英]C++ - Reading a Table from a Text File
我一直在嘗試為 class 分配作業,我應該從名為 table.txt 的文件中讀取包含學生姓名和成績的表格,並將值存儲在 C++ 變量中,然后我需要從那里操作成績數據,然后通過表格將其全部呈現給用戶。 當我運行我的代碼時,會創建必要的表,但沒有出現數據。 行數保持為 0,Class 平均值達到 -nan(ind)。 恐怕這可能是打開文本文件本身的問題,但我不確定。 這是我需要你幫助的地方,任何幫助都將不勝感激,如果我的請求看起來很簡單或者我的代碼很可怕,我深表歉意,因為我是編程領域的新手!
表.txt
Anderson 91.5 95
Blake 75.5 90
Cheg 0 0
Dang 95 85
Engberg 80 100
Farris 55 90
Garcia 93.6 90.5
Hadad 65 60
Ionescu 100 95.5
Johnson 75 90
Kaloo 75 85
Lagos 55.5 80
Mikhailov 75 83.5
Nguyen 95 100
O'Neil 85 70
我當前的代碼
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <iomanip>
using namespace std;
int main()
{
ifstream fin("table.txt");
int numStudents = 0;
double avgNumGrade = 0.0;
cout << setw(30) << "STUDENT STATISTICS" << endl;
cout << setfill(' ') << setw(15) << "Student Name" << setw(15) << "Total Points" << setw(15) << "Numeric Grade" << setw(15) << "Letter Grade" << endl;
string line;
while (getline(fin, line)) {
string name;
double testGrade;
int assnGrade;
double totalPoints;
double numGrade;
char letterGrade;
fin >> name;
cout << setw(10) << left << name << setfill(' ');
fin >> testGrade;
fin >> assnGrade;
totalPoints = testGrade + assnGrade;
numGrade = totalPoints / 2;
if (numGrade <= 100 && numGrade >= 89.5) {
letterGrade = 'A';
}
else if (numGrade <= 89.49 && numGrade >= 79.5) {
letterGrade = 'B';
}
else if (numGrade <= 79.49 && numGrade >= 69.5) {
letterGrade = 'C';
}
else if (numGrade <= 69.49 && numGrade >= 59.5) {
letterGrade = 'D';
}
else if (numGrade <= 59.49 && numGrade >= 0) {
letterGrade = 'F';
}
cout << setw(5) << right << setprecision(3) << totalPoints << setfill(' ') << numGrade << setfill(' ');
cout << setw(5) << letterGrade << endl;
numStudents++;
avgNumGrade = avgNumGrade + numGrade;
}
avgNumGrade = avgNumGrade / numStudents;
cout << setw(30) << "CLASS STATISTICS" << endl;
cout << setw(15) << left << "Number:" << setfill(' ') << numStudents << endl;
cout << setw(15) << left << "Average:" << setfill(' ') << avgNumGrade << endl;
fin.close();
}
繼續我的評論,每當您進行任何輸入(或對代碼的持續操作至關重要的任何其他操作)時,您都必須驗證每一步。 在您繼續之前,必須驗證並確認所有作為后續操作的先決條件的內容才能成功。 在您的情況下,在您嘗試從文件中讀取之前,驗證您的輸入文件是否實際上已打開以進行讀取至關重要。
(注意:我懷疑在您的情況下,文件table.txt
運行可執行文件的當前工作目錄中。如果您使用 IDE 來編譯和運行代碼,這可能是一個挑戰。確保您知道你的可執行文件是在哪里創建的,並確保你的table.txt
在那個目錄中——或者更好的是,打開一個終端,從那里編譯並運行你的代碼並消除所有疑問......)
要驗證文件是否已打開,請使用std::basic_ifstream::is_open (保存 cppreference.com 書簽,並在您有疑問的 C++ 的每個部分中參考它,這是網絡上最好的)。 嘗試打開文件后,只需:
if (!fin.is_open()) {
std::cerr << "error: file open failed '" << argv[1] <<"'.\n";
return 1;
}
當我們討論打開文件時,永遠不要硬編碼文件名。 要么將要讀取的文件名作為參數傳遞給main()
(這就是argc
和argv
的用途),要么提示用戶輸入文件名並將其作為輸入。 您不必為了讀取不同的文件名而重新編譯程序。 這很簡單,例如
int main (int argc, char **argv) {
if (argc < 2) { /* validate one argument given for filename */
std::cerr << "error: insufficient arguments.\n"
"usage: " << argv[0] << " filename.\n";
return 1;
}
...
std::ifstream fin (argv[1]);
if (!fin.is_open()) { /* validate file open for reading */
std::cerr << "error: file open failed '" << argv[1] <<"'.\n";
return 1;
}
現在,正如您驗證文件已打開以供讀取一樣,您必須驗證您已將每行中的三個值讀取到name
、 testGrade
和assnGrade
中。 雖然有點脆弱,但最簡單的方法就是:
/* validate the read of each value */
while (fin >> name >> testGrade >> assnGrade) {
...
這確保您收到每個name
、 testGrade
和assnGrade
的有效輸入。 從文件的任何行中的任何雜散或損壞的數據將導致讀取失敗並且將導致文件中所有剩余行的讀取失敗的角度來看,它是脆弱的。 (最好用getline()
讀取每一行並從該行創建一個stringstream()
,然后從 stringstream 解析值)
您的letterGrade
測試過於復雜。 您不需要檢查上限作為每個等級的條件。 if()... else if()...
條件將按順序進行測試。 因此,如果numGrade
不是>= 89.5
,您只需接下來檢查它是否>= 79.5
等等...,例如
if (numGrade >= 89.5) /* no need for an upper bounds compare */
letterGrade = 'A';
else if (numGrade >= 79.5)
letterGrade = 'B';
else if (numGrade >= 69.5)
letterGrade = 'C';
else if (numGrade >= 59.5)
letterGrade = 'D';
else
letterGrade = 'F';
您對std::setfill(' ');
使用錯位了。 默認填充是空格。 除非您希望將其設置為空格以外的內容,否則無需更改填充。 當您使用std::cout
輸出信息時,對於 output 的任何連續塊,您永遠不需要多次調用std::cout
。 例如,您可以將 output 每個學生的數據與:
std::cout << " " <<
std::setw(10) << std::left << name <<
std::setw(15) << std::right << std::fixed <<
std::setprecision(2) << totalPoints <<
std::setw(15) << numGrade <<
std::setw(15) << (char)letterGrade << '\n';
如果您注意到,我們為例如std::cout
指定了一個顯式命名空間,請參閱Why is “using namespace std;” 被認為是不好的做法? .
如果你把它放在一起,你可以這樣:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
int main (int argc, char **argv) {
if (argc < 2) { /* validate one argument given for filename */
std::cerr << "error: insufficient arguments.\n"
"usage: " << argv[0] << " filename.\n";
return 1;
}
size_t numStudents = 0;
double avgNumGrade = 0., testGrade, assnGrade;
std::string name {};
std::ifstream fin (argv[1]);
if (!fin.is_open()) { /* validate file open for reading */
std::cerr << "error: file open failed '" << argv[1] <<"'.\n";
return 1;
}
/* only one call to std::cout is necessary */
std::cout << std::setw(30) << "STUDENT STATISTICS\n" <<
std::setw(15) << "Student Name" <<
std::setw(15) << "Total Points" <<
std::setw(15) << "Numeric Grade" <<
std::setw(15) << "Letter Grade\n";
/* validate the read of each value */
while (fin >> name >> testGrade >> assnGrade) {
char letterGrade = 0;
double totalPoints = testGrade + assnGrade,
numGrade = totalPoints / 2.;
if (numGrade >= 89.5) /* no need for an upper bounds compare */
letterGrade = 'A';
else if (numGrade >= 79.5)
letterGrade = 'B';
else if (numGrade >= 69.5)
letterGrade = 'C';
else if (numGrade >= 59.5)
letterGrade = 'D';
else
letterGrade = 'F';
std::cout << " " <<
std::setw(10) << std::left << name <<
std::setw(15) << std::right << std::fixed <<
std::setprecision(2) << totalPoints <<
std::setw(15) << numGrade <<
std::setw(15) << (char)letterGrade << '\n';
numStudents += 1;
avgNumGrade += numGrade;
}
avgNumGrade /= numStudents;
std::cout << '\n' << std::setw(30) << "CLASS STATISTICS\n" <<
" " << std::setw(12) << std::left << "Number:" <<
numStudents << "\n " <<
std:: setw(12) << std::left << "Average:" <<
avgNumGrade << '\n';
}
示例使用/輸出
使用文件dat/table.txt
中的示例數據,您可以:
$ ./bin/student_table dat/table.txt
STUDENT STATISTICS
Student Name Total Points Numeric Grade Letter Grade
Anderson 186.50 93.25 A
Blake 165.50 82.75 B
Cheg 0.00 0.00 F
Dang 180.00 90.00 A
Engberg 180.00 90.00 A
Farris 145.00 72.50 C
Garcia 184.10 92.05 A
Hadad 125.00 62.50 D
Ionescu 195.50 97.75 A
Johnson 165.00 82.50 B
Kaloo 160.00 80.00 B
Lagos 135.50 67.75 D
Mikhailov 158.50 79.25 C
Nguyen 195.00 97.50 A
O'Neil 155.00 77.50 C
CLASS STATISTICS
Number: 15
Average: 77.69
如果您還有其他問題,請仔細查看並告訴我。
我同意@David C。 Rankin 認為最可能的問題是您的文件打開失敗。 而且,最可能的原因是您的代碼可能不是從您認為的目錄中運行的。
在大多數 IDE 環境(VS、CLion)中,可執行文件的“當前工作目錄”不是源代碼文件所在的目錄,而是通常位於文件夾樹中的構建文件夾,其名稱取決於構建(發布或調試)。
檢查文件打開的返回狀態。 如果失敗,請弄清楚如何將代碼的當前工作目錄更改為 table.txt 所在的位置。 或者,找到 .exe 文件並將 table.txt 移動到該文件夾中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.