繁体   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