0

全部,

我正在用 C++ 做一些编程练习,然后尝试将它们翻译成 C# 和其他语言来复习。

这个例子来自Ch。11.2 来自 Nell Dale 和 Chip Weems 的“使用 C++ 编程和解决问题”(第五版),第 521-522 页。这是一个接收这个文件的程序(学生姓名、GPA、四个成绩和课程成绩)——我们称之为rec.in

玛丽雅培 3.4 80 90 60 99 B

鲍勃·贝克 4.0 90 90 90 95 A

萨莉卡特 2.0 70 70 70 65 C

比尔丹斯克 3.0 65 50 60 70 D

乔艾伦·祖雷克 1.9 90 95 80 99 A

并且(或多或少)只是将其复制到另一个文件中。

这是执行此操作的代码:

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

const int MAX_STUDENTS = 150;

enum GradeType{A,B,C,D,F};

struct StudentRec
{
    string stuName;
    float gpa;
    int examScore[4];
    GradeType courseGrade;
};
StudentRec gradeBook[MAX_STUDENTS];

int main()
{
    StudentRec record[MAX_STUDENTS];
    ofstream outFile;
    outFile.open("rec.out");
    ifstream inFile;
    inFile.open("rec.in");
    for (int count = 0; count < MAX_STUDENTS; count++)
    ReadValues(inFile, record[count]);
    for (int count = 0; count <= MAX_STUDENTS; count++)
    PrintValues(outFile, record[count]);
    inFile.close();
    outFile.close();
    return 0;
}

void ReadValues(ifstream& inFile, StudentRec& record)
{
    char letter;
    char throwAway;

    getline(inFile, record.stuName);
    inFile >> record.gpa>>record.examScore[0]
    >> record.examScore[1] >> record.examScore[2]
    >> record.examScore[3] >> letter;

    inFile.get(throwAway);

    switch(letter)
    {
        case 'A' : record.courseGrade = A;
           break;
        case 'B' : record.courseGrade = B;
           break;
        case 'C' : record.courseGrade = C;
           break;
            case 'D' : record.courseGrade = D;
           break;
        case 'F' : record.courseGrade = F;
               break;
   }
}

void PrintValues(ofstream& outFile, StudentRec& record)
{
    outFile << record.stuName << endl;
    outFile << record.gpa << ' ' << record.examScore[0] << ' '
        << record.examScore[1] << ' '
        << record.examScore[2] << ' '
        << record.examScore[3] << ' ';
    switch (record.courseGrade)
    {
        case A : outFile << 'A';
        break;
        case B : outFile << 'B';
        break;
        case C : outFile << 'C';
        break;
        case D : outFile << 'D';
        break;
        case F : outFile << 'F';
        break;
    }

outFile << endl;
}

我开始翻译 C#。我以前做过 C# 工作,但不是控制台工作。结果,有点……乱七八糟……翻译东西。我对getline在 C# 中匹配 C++ 的功能感到有点困惑。当然,有System.IO.StreamReader,但它一次只读取一个文件一行。getline如上所示,以流格式执行。这使用单词/字符之间的空格/换行符将其分解。如何使用 C# 的库选择获得相同的结果?我并不完全同意System.IO.StreamReader解决方案。

这是我所拥有的开始:

using System;
using System.IO;

namespace _112a
{
    class Program
    {
        const int MAX_STUDENTS = 150;
        enum GradeType{A,B,C,D,F};
        struct StudentRec{
            string stuName;
            float gpa;
            GradeType courseGrade;
        };

        StudentRec[] gradeBook = new StudentRec[MAX_STUDENTS];

        static void Main(string[] args)
        {
            StudentRec[] record = new StudentRec[MAX_STUDENTS];
            System.IO.StreamWriter outFile = new System.IO.StreamWriter("rec.out"); 
            System.IO.StreamReader inFile = new System.IO.StreamReader("rec.in");

            for (int count = 0; count < MAX_STUDENTS; count++)
            {
                ReadValues(inFile, record[count]);
            }

            for (int count = 0; count <= MAX_STUDENTS; count++)
            {
                PrintValues(outFile, record[count]);
            }

            inFile.Close();
            outFile.Close();
        }

        static void ReadValues(StreamReader inFile, StudentRec record)
        {
            char letter;
            char throwAway;

            /*can't use getline, what do?*/

            inFile.ReadLine();

            switch (letter)
            {
                case 'A':
                    record.courseGrade = (GradeType)Enum.Parse(typeof(GradeType), "A");
                    break;
                case 'B':
                    record.courseGrade = (GradeType)Enum.Parse(typeof(GradeType), "B");
                    break;
                case 'C':
                    record.courseGrade = (GradeType)Enum.Parse(typeof(GradeType), "C");
                    break;
                case 'D':
                    record.courseGrade = (GradeType)Enum.Parse(typeof(GradeType), "D");
                    break;
                case 'F':
                    record.courseGrade = (GradeType)Enum.Parse(typeof(GradeType), "F");
                    break;
            }


        }

        static void PrintValues(StreamWriter outFile, StudentRec record)
        {
            outFile.Write("{0}\n{1} {2} {3} ", record.stuName, record.examScore[0], record.examScore[1], record.examScore[2], record.examScore[3]);

            switch(record.courseGrade)
            {
                case GradeType.A: 
                    outFile.Write("A\n");
                    break;
                case GradeType.B: 
                    outFile.Write("B\n");
                    break;
                case GradeType.C: 
                    outFile.Write("C\n");
                    break;
                case GradeType.D:
                    outFile.Write("D\n");
                    break;
                case GradeType.F:
                    outFile.Write("F\n");
            }
        }
    }
}

关于您在方法中看到的开关结构,我还有一个问题PrintValues:在每种情况下,它都在 C++ 文件中指定,它只获取该record.courseGrade字段并将其与后面指定的参数进行比较case。C# 作为一种类型强的语言,不会让这种情况发生……如果将char'A' 与进行比较record.courseGrade,则会出现类型不匹配错误。或者,将其保留为不带单引号(因此将其保留为record.courseGrade字段)会出现错误“The name 'A' does not exist in the current context.我该如何最好地处理这个?-已解决

我对 ReadLine() 的困惑源于它的工作方式。在这种情况下,您有多个StudentRec结构成员被填充,但它们是结构中的独立实体。我担心会做类似的事情:

StudentRec.stuName = inFile.ReadLine();

StudentRec.stuName填充了整行,其他结构字段中没有任何内容。

此外,我们还有letter未分配并用作开关的问题;这是因为它从未从ReadLine. 但是,这是一个学术解决方案,如果ReadLine确实将事情分开的话。

4

2 回答 2

3

您想分别使用 StreamReader 和 Writer。您可能正在寻找的方法是ReadLineWriteLine,StreamReader 文档底部有一个可以满足您需求的工作示例。您只需要更改 while 循环内的文件路径和逻辑。文档可以在这里找到;

http://msdn.microsoft.com/en-us/library/system.io.streamreader.aspx

StreamWriter 文档在这里,并在底部有一个同时使用读取器和写入器的示例;

http://msdn.microsoft.com/en-us/library/system.io.streamwriter.aspx

所有方法都有逻辑名称,并且比它们的 c++ 对应物更易于实现。

我相信您的开关会导致编译错误,因为案例需要case "A":代替case A:. 如果您在其中放置文字,它将进行字符串比较。它不明白 A 是什么,除非那是局部变量的名称。

编辑:读取文件的示例代码;

// we need to alloc/init the streams inside for using blocks so they're properly disposed of
List<string> grades = new List<string>();
using (StreamReader inFile = new StreamReader("rec.in"))
{
     string line;
     while ((line = inFile.ReadLine()) != null)
     {
         string[] tokens = line.Split(' '); // split line on whitespace
         if (tokens.Length > 7)
             grades.Add(tokens[7]);
     }
}

using (StreamWriter outFile = new StreamWriter("rec.out"))
{
      foreach (string s in grades)
          outFile.WriteLine(s);
}

我上面使用的技术并不是完全必要的,例如,您可以在 StreamReaders 中嵌套使用 StreamWriter 并在同一范围内进行写入。在 C# 版本中,您将逐行遍历文件的内容。每次我们得到一条线时,我们都想在空格上分割它以获得它的子组件。然后我将字母等级添加到列表中。我们不希望输出字符串中出现换行符,因为WriteLine它会自动执行。在我们读取文件之后,我们循环遍历列表中的每个字符串并将其写入输出文件。

于 2013-05-28T23:00:42.390 回答
1

对于 switch 的问题,试试这个:

case GradeType.A: 
    outFile.Write("A\n");
    break;

您使用枚举,并且必须将其提供给 switch 案例。

对于其他问题,如果要处理文件中的每一行,则必须将其分配给字符串,然后对其进行处理,以通过拆分等方法提取所需的信息。

string line = inFile.ReadLine();
string[] data = line.Split(' ');
record.stuName = string.Format("{0} {1}", data[0], data[1]);
record.gpa = float.Parse(data[2]);
....
于 2013-05-28T23:16:00.837 回答