Getline在csv读取非常奇怪

时间:2009-11-18 17:00:44

标签: c++ file csv getline

我正在尝试使用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;
    }
}

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

5 个答案:

答案 0 :(得分: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被初始化为某种东西。

答案 1 :(得分:4)

一些意见:

不要自己编写。

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循环中。这是因为read的结果返回对流的引用。这可以自动转换为可以在布尔上下文中使用的对象(转换测试以查看流是否有问题)。因此,读取失败会使流处于一种状态,导致它在布尔上下文中转换为等效的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-string'进行比较。编译器如何设法编译这个我不确定 我想到了几个选项,但它不明显的事实使它成为错误的可能来源。另请注意,这不是您比较两个字符串的方式(除非一个是std :: string)。

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

答案 2 :(得分: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。

答案 3 :(得分:1)

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

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

为了使其有用,student类应包含相当多的逻辑,例如如何从流中读取student或显示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;
}               

请注意,特别是,主要只处理“整个”student作为逻辑实体 - 它永远一旦看起来在student对象的“内部”在其组成部分。

答案 4 :(得分:0)

通过在调用

中将分隔符设置为','
getline(file, tempname, ',');

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

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