C++ 在csv中读取Getline非常奇怪

C++ 在csv中读取Getline非常奇怪,c++,file,csv,getline,C++,File,Csv,Getline,我正在尝试使用get行读取csv,以提取三个用逗号分隔的变量。姓名、课程和年级 我读的第一行很好,但它加入了奇怪的换行符,并将格式发送到集群中 这是我的密码: #include "header.h" string student::GetCourse() { return course; } string student::GetName() { return name; } string student::GetGrade() { return grade; }

我正在尝试使用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;
    }
}
任何想法,我都很难读懂这个文件。

好了,开始吧

如果文件!=\n比较是荒谬的。它不会做你认为它会做的事。 等级后的分隔符不是“,”,而是“\n”。 虽然file.eof不正确。只有在EOF已经发生后才进行检查。您应该检查getline的返回值 也

通常在C++中,你做STD::IFFStudio文件。您不需要单独调用open。 您不需要将std::string初始化为。这是自动发生的。即使你必须这么做,你也应该写

std::字符串a=,b=,c=

如果您使用std::stringa,b,c=something,那么只有c被初始化为something


第一:您没有检查输入是否在任何地方成功。见鬼,你甚至不检查文件是否可以打开:

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;
  }
  // ...
}
然后通过字符串流读取这些行。我会将代码逐行读取放到它自己的函数中:

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

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

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

我建议使用默认分隔符来读取整行内容,然后使用“,”作为分隔符并使用if!file.eof
确定您何时读完该文件。

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

现在,您的学生类基本上在尽最大努力模仿哑数据,即只是一个简单的结构,但语法更难看-您为每个成员使用一个get/set对,但他们没有添加任何内容。学生类本身就像一个简单的结构一样愚蠢。学生的所有逻辑仍然在学生课堂之外

为了使其有用,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;
    }
};
这使主要问题变得相当简单:

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只将整个学生作为一个逻辑实体来处理——它从不在学生对象内部查看其组成部分。

一些评论:

不要写你自己的那一类。 STL有自己的内置排序算法。 您只需指定对象之间的关系:

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

   std::sort(list.begin(),list.end());
不要使用幻数: 你是真的忽略了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')
使用stringstream将线拆分为多个部分 我不知道你在这里想干什么

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;
}
在这里,您将文件与“C字符串”进行比较。我不确定编译器是如何编译的。 有几种选择浮现在脑海中,但事实上它并不明显,这可能是一个bug的来源。还要注意,这不是比较两个字符串的方式,除非其中一个是std::string


我认为编译器会将文件转换为指针,并将其与C字符串进行比较,因为这也只是一个指针。您可能认为这有点奇怪,但有一个操作符可以将文件转换为空*。指针不指向任何有意义的,但它是空的或不空的,可以与char *指针进行比较,从而得到一个真值,因为它不等于字符串\n。< /p>编辑好,以正确地格式化所有代码,但我怀疑任何人都会读取它:为什么在C++中这样做?你应该使用其他任何东西:perl、python、ruby、tcl……Jon,我猜这是一个家庭作业问题。我更喜欢他的答案。它更详细:这不是一个好主意“whilefile.good”,因为它测试得太早或太迟。你应该测试行动的结果。”因此,如果它失败,则永远不会进入循环。通过良好的测试,您需要在阅读后再次测试。读取文件的经典反模式。在初始化字符串部分,您可能想执行以下操作?std::字符串a=b=c=;这是行不通的,因为它假设b和c已经定义好了。您必须这样做:std::stringa,b,c;a=b=c=;但这不再是初始化,这是分配。@Martin York,我知道我甚至在回答中提到了它:。这是关于初始化的。@Martin:我修正了一些打字错误,
我希望没问题。我同意你所说的每一点。@sbi:总是愿意接受帮助。谢谢你的更正。
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
    }
}
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::copy(StudentList.begin(),StudentList.end(),
          std::ostream_iterator<student>(std::cout,"\n")
         );
if (file != "\n")