0

我正在尝试使用 get 行读取 csv 以提取用逗号分隔的三个变量。姓名、课程和成绩。

我在第一行读得很好,但它插入了奇怪的换行符并将格式发送到一个集群中。

这是我的代码:

#include "header.h"

string student::GetCourse() {
    return course;
}

string student::GetName() {
    return name;
}

string student::GetGrade() {
    return grade;
}

void student::setname(string n) {
    name = n;
}

void student::setCourse(string c) {
    course = c;
}

void student::setGrade(string g) {
    grade = g;
}
void sort (vector <student> &List) {

    student temp;
    int first = 1;
    int vectorLength = List.size() - 1;

    for (int i = vectorLength; i > 0; i--) {
        first = i;
        for (int j = 0; j < i; j++) {
            if (List[j].GetName() > List[first].GetName())
            first = j;
        }
        temp = List[first];
        List[first] = List[i];
        List[i] = temp;
    }

}

void main () {
    ifstream file;
    vector <student> StudentList;

    file.open("short.txt");

    while (!file.eof()) {

        file.ignore(8196,'\n');

        string tempname, tempgrade, tempcourse = "";

        if (file != "\n") {
            getline(file, tempname, ',');
            getline(file, tempcourse, ',');
            getline(file, tempgrade, ',');
        }

        student s;
        s.setCourse(tempcourse);
        s.setname (tempname);
        s.setGrade (tempgrade);

            StudentList.push_back(s);

    }
    //sort (StudentList);

    for (int i = 0; i < StudentList.size(); i++) {
        cout << StudentList[i].GetName() << " " << StudentList[i].GetCourse() << " " << StudentList[i].GetGrade() << endl;
    }
}

任何想法,我都很难阅读这个文件。

4

5 回答 5

7

嗯,这就去

  • if (file != "\n")比较是荒谬的。它不会做你认为它会做的事情。
  • 你的分界符不是等级后的',',是的'\n'
  • while (!file.eof())是不正确的。这只在 EOF 已经发生之后才检查它。你应该检查你的返回getline()

  • 通常在 C++ 中你会这样做std::ifstream file("short.txt");。你不需要open()单独打电话。
  • 您不需要初始化std::string为“”。这是自动发生的。即使你必须这样做,那么你应该写

    std::string a = "", b = "", c = "";.

    如果你这样做了,std::string a, b, c = "something"那么只有 c 被初始化为一些东西。

于 2009-11-18T17:13:18.997 回答
5

一些评论:

不要编写自己的排序。

STL 有自己的内置排序算法。
您所要做的就是指定对象之间的关系:

bool operator<(student const& lhs,student const& rhs)
{
    return lhs.GetName() < rhs.GetName();
}
// Now a sort is:

   std::sort(list.begin(),list.end());

不要使用:while (!file.eof())

这是读取文件的标准反模式。
问题是测试太早或晚了两个。如果您还没有阅读任何内容,那是两个早,因为什么都没发生。如果您已经阅读了某些内容,那么已经为时已晚,因为您已经对阅读的项目进行了处理(但失败了)。

最好的方法是将读取放入 while 循环中。这是因为读取的结果返回了对流的引用。这可以自动转换为可以在布尔上下文中使用的对象(转换测试以查看流是否有问题)。因此,读取失败将使流处于一种状态,该状态会导致它在布尔上下文中转换为等效的 false。

std::string line;
while(std::getline(file,line))
{
   // loop only entered if getline() worked.
   // Thus we know that we have a good value in line.
   // use line
}

不要使用幻数:

你真的忽略了 8000 个字符还是只是想放弃一行?

file.ignore(8196,'\n');

您有两种选择:

std::string ignoreLine;
std::getline(file,ignoreLine);

// Dont use a magic number but use a number that is guranteed not to fail.
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n')

不要偷懒:

编程的主要内容是编写可维护的代码。
使用这种初始化(相对普遍)被谴责为懒惰。将每个声明放在单独的行上。它使代码更易于阅读。

string tempname, tempgrade, tempcourse = "";

// Like this:
std::string tempname;
std::string tempgrade;
std::string tempcourse;

使用字符串流将线分成几部分

我不确定你在这里尝试什么?

if (file != "\n")
{   getline(file, tempname, ',');
    getline(file, tempcourse, ',');
    getline(file, tempgrade, ',');
}

我认为如果我们将它与上面的循环结合起来会更容易阅读:

std::string line;
while(std::getline(file,line))
{
    std::stringstream  linestr(line);

    if (getline(linestr, tempname, ',') &&
        getline(linestr, tempcourse, ',') &&
        getline(linestr, tempgrade, ',')
       )
    {
        // Here we have read a line.
        // And successfully retrieved three comma separated values from the line
    }
}

当机会出现时,用标准算法替换循环

此打印循环可以替换为 std::copy()

for (int i = 0; i < StudentList.size(); i++)
{        cout << StudentList[i].GetName() << " " 
              << StudentList[i].GetCourse() << " " 
              << StudentList[i].GetGrade() << endl;
}

您需要做的就是为您的类定义一个输出运算符。

std::ostream& operator<<(std::ostream& str,student const& data)
{
    str << data.getName() << " "
        << data.getCourse() << " "
        << data.getGrade() << " "; // No newline here.
    return str;
}

现在我们可以将向量复制到 std::cout

std::copy(StudentList.begin(),StudentList.end(),
          std::ostream_iterator<student>(std::cout,"\n")
         );

主要错误。

我看到的主要错误是这一行:

if (file != "\n")

在这里,您将文件与“C 字符串”进行比较。我不确定编译器是如何编译的。
有几个选项浮现在脑海中,但它并不明显这一事实使其成为错误的可能来源。另请注意,这不是您比较两个字符串的方式(除非一个是 std::string)。

我认为编译器会将文件转换为指针并将其与“C-String”进行比较(因为这也只是一个指针)。您可能认为这有点奇怪,但是有一个运算符可以将文件转换为 void*。该指针不指向任何有意义的东西,但要么是 NULL 要么不是 NULL,并且可以与 char* 指针进行比较,从而得到一个真值(因为它从不等于字符串“\n”)。

于 2009-11-18T22:05:18.067 回答
2

首先:您不是在检查输入是否在任何地方成功。哎呀,您甚至不检查文件是否可以打开:

int main () {                          // it's int main()!
  ifstream file("short.txt");
  if(!file.good()) {
    std::cerr << "couldn't open \"short.txt\"\n";
    return 1;
  }

  vector <student> StudentList;
  for(;;) {
    // ...
  }
  if( !file.eof() ) {
    std::cerr << "error reading before eof!\n";
    return 2;
  }
  // ...
}

然后:通常在该循环中首先读取行更容易:

for(;;) {
  std::string line;
  std::getline(file, line);
  if(!file) break;
  // ...
}

然后通过字符串流从这些行中读取。我会将读取的代码放入它自己的函数中:

std::istream& read_line(std::istream& is, StudentList& list)
{
  std::string value1, value2, value3;
  std::getline(is, value1, ',');
  std::getline(is, value2, ',');
  std::getline(is, value3, ',');
  if(is)
    StudentList.push_back(...);
}

// ...
for(;;) {
  std::string line;
  std::getline(file, line);
  if(!file) break;

  std::istringstream iss(line);
  read_line(iss, StudentList);
  if(!iss) break;
}
// ...

HTH。

于 2009-11-18T17:17:21.003 回答
1

你已经得到了很多答案。虽然他们的建议肯定是对您现在所做的事情的改进,但我会以与他们建议的方式有所不同的处理方式。

现在你的student类基本上是在尽最大努力模仿“哑数据”(即只是一个普通的结构),但语法更丑——你为每个成员使用了一个 get/set 对,但他们没有添加任何东西。类student本身就像一个简单的结构一样“愚蠢”。的所有逻辑student仍在student课堂之外。

为了使其有用,student该类应该包含相当多的相关逻辑,例如如何从流中读取 a student,或在不同的流中显示 a student

class student { 
    std::string name, course, grade;
public:

    bool operator<(student const &other) const {
        return name < other.name;
    }

    friend std::ostream &operator<<(std::ostream &os, student const &st) { 
        return os << st.name << " " << st.course << " " << st.grade;
    }

    friend std::istream &operator>>(std::istream &is, student &st) { 
         std::string temp;
         is >> temp;
         std::istringstream t(temp);
         std::getline(t, st.name, ',');
         std::getline(t, st.course, ',');
         std::getline(t, st.grade);
         return is;
    }
};

这使得 main 相当简单:

int main() { 
    std::ifstream in("short.txt");
    std::vector<student> students;

    std::copy(std::istream_iterator<student>(in),
              std::istream_itertor<student>(),
              std::back_inserter(students));
    std::sort(students.begin(), students.end());
    std::copy(students.begin(), students.end(), 
        std::ostream_iterator<student>(std::cout, "\n"));
    return 0;
}               

请特别注意,main 仅将“整体”student作为逻辑实体处理——它从未student在对象的组成部分“内部”查看过。

于 2009-11-18T18:01:06.420 回答
0

通过在调用中将分隔符设置为“,”

getline(file, tempname, ',');

你不是一次读一整行。'\n' 是默认分隔符,通过使用默认值,您将获得整行,而不仅仅是其中的一部分。

我建议使用默认分隔符读取整行,然后使用“,”作为分隔符将该行分成标记,并用于if(!file.eof) 确定您何时完成读取文件。

于 2009-11-18T17:25:44.387 回答