簡體   English   中英

C++ - 從文本文件中讀取表格

[英]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() (這就是argcargv的用途),要么提示用戶輸入文件名並將其作為輸入。 您不必為了讀取不同的文件名而重新編譯程序。 這很簡單,例如

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

現在,正如您驗證文件已打開以供讀取一樣,您必須驗證您已將每行中的三個值讀取到nametestGradeassnGrade中。 雖然有點脆弱,但最簡單的方法就是:

    /* validate the read of each value */
    while (fin >> name >> testGrade >> assnGrade) {
        ...

這確保您收到每個nametestGradeassnGrade的有效輸入。 從文件的任何行中的任何雜散或損壞的數據將導致讀取失敗並且將導致文件中所有剩余行的讀取失敗的角度來看,它是脆弱的。 (最好用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM