【问题标题】:Extract information from a colon delimited file - C++从冒号分隔的文件中提取信息 - C++
【发布时间】:2013-12-05 07:22:57
【问题描述】:

我正在尝试从冒号分隔的文件中将信息提取到类对象中。文件的每一行都以相同的格式设置。以下是文件的前几行:

s:Charles:Babbage:80:530213286:1133764834:mechanical engineering:3.8
e:Marissa:Meyer:37:549114177:53321:ceo:4456000
s:Alonzo:Church:92:586312110:1100539644:mathematics:4.0
e:Dana:Ulery:74:573811211:23451:engineer:124569

这是一个学校项目,目的是教我们关于类继承的知识。我们有一个基类 Person 和两个子类 Student 和 Employee。我们应该将学生的信息导入并存储到 Student 对象中,将员工信息导入并存储到 Employee 对象中。每个类都有一个对象数组;我将学生排序到 Student 对象数组中,对于员工也是如此,此外还将所有人添加到 People 对象数组中。

我不知道如何使用分隔逗号获取每条信息。现在我正在尝试使用 .getline 但它似乎不起作用。如何使用此函数(或其他函数)将分隔符之间的信息提取到 char 数组中?以下是我目前对于员工数据的情况:

ifstream fin;
char* tempImport;
tempImport = new char[50];
int* tempIntArray;
tempIntArray = new int[10];
double tempDouble;
int tempInt;

  // get the specifier of student or employee
  fin.getline(tempImport, ':');

  if(tempImport[0]=='e'){
     // get first name
     fin.getline(tempImport, ':');
     employees[employeeIndex].setFirstName(tempImport);
     allPeople[personIndex].setFirstName(tempImport);
     // get last name
     fin.getline(tempImport, ':');
     employees[employeeIndex].setFirstName(tempImport);
     allPeople[personIndex].setFirstName(tempImport);
     // get age
     fin.getline(tempImport, ':');
     employees[employeeIndex].setAge(tempImport[0] - 0);
     allPeople[personIndex].setAge(tempImport[0] - 0);
     // get SSN
     fin.getline(tempImport, ':');
     for(int i=0;i<9;i++){
        tempIntArray[i] = tempImport[i] - 0;
        }
     employees[employeeIndex].setSsn(tempIntArray);
     allPeople[personIndex].setSsn(tempIntArray);
     // get Employee ID
     fin.getline(tempImport, ':');
     for(int i=0;i<5;i++){
        tempIntArray[i] = tempImport[i] - 0;
        }
     employees[employeeIndex].setEmpID(tempIntArray);
     // get title
     fin.getline(tempImport, ':');
     employees[employeeIndex].setTitle(tempImport);
     // get salary
     fin >> tempDouble;
     employees[employeeIndex].setSalary(tempInt);
     employeeIndex++;
     personIndex++;
     }

【问题讨论】:

  • 使用调试器单步执行程序时发生了什么?
  • 请,请,请,请使用'0'而不是48。很多人没有意识到48是字符零的ASCII码的十进制值。
  • 为什么要使用new 运算符和动态内存?这是 C++,不是 Java。仅在绝对必要时使用动态内存。研究“内存泄漏 c++”以获取有关副作用的更多信息。
  • 动态内存使用是项目的要求。增加我们搞砸的几率,从而从中吸取教训。
  • 不是你原来的问题,但我注意到你似乎设置了两次名字,而不是设置名字然后设置姓氏。 (我猜这是一个复制粘贴错误......):)

标签: c++ ifstream getline


【解决方案1】:

当您调用ifstream::getline() 时,您似乎缺少了一个参数。看: http://www.cplusplus.com/reference/istream/istream/getline/

您需要该方法的 3 参数版本才能指定分隔符。当您调用 2 参数版本时,它会将 ':' 解释为流大小。 (基本上,':' 只是解析为冒号的 ASCII 代码,因此该数字被传入。您真正想要的 streamsize 是 tempImport 缓冲区的长度。)

但是,如果我可以建议(并且您的任务允许),该函数的 std::getline() 版本可能会更好。 (它允许您使用 std::string 而不是 char*,这是一种更 C++ 的处理方式。您也不必担心输入是否大于缓冲区。)这是文档对此: http://www.cplusplus.com/reference/string/string/getline/

所以基本上你可以这样做:

std::string tempImport;
std::getline(fin, tempImport, ':');

作为调试建议,您可以在每次调用 getline() 后打印 tempImport(无论您使用哪种类型)。在你提交之前把它们拿出来,但是这些打印语句可以帮助你同时调试你的解析。

std::stderr << "getline(): " << tempImport << std::endl;

编辑:

关于下面的评论,我能够编译它。 (它没有做任何有用的事情,但表明 std::getline() 确实存在并编译。)它为你编译了吗?

#include <fstream>

int main (int argc, char** argv)
{
        std::ifstream ifs;
        std::string str;
        std::getline(ifs, str, ':');
        return 0;
}

【讨论】:

  • 当我用这种方式尝试时,它告诉我 std::getline 没有匹配的函数。我该如何解决这个问题?
  • 我在上面答案的末尾放了一个代码sn-p。它使用std::getline() 并为我正确编译。看看它是否适合你......(我把它放在上面是因为多行代码 sn-ps 在 cmets 中不起作用。):(
  • 感谢您的帮助,戴夫。编译的代码,我发现我的问题是 tempImport 是一个 char*。有没有办法用指针来使用getline函数?
  • 我的意思是建议您使用std::string 而不是char*。 :) 这种方式更像 C++(而不是 C-ish),也意味着您不必担心分配字符串缓冲区或溢出该缓冲区。但是,如果您仍然喜欢使用 char*(或者您的赋值指令要求您这样做),那么您可以使用 istream::getline() 成员函数 (cplusplus.com/reference/istream/istream/getline),但一定要包含 streamsize 参数(一个整数)告诉它你的char*缓冲区有多少字节(例如,tempImport = new char[50];时为50)
【解决方案2】:

如果你能原谅我这么说的话,你似乎被教导了“面向对象编程”的最糟糕的模仿之一(尽管如果它有任何安慰的话,它也是一个相当普遍的模仿)。

就个人而言,我认为我会写一些完全不同的东西。

我可能会从消除所有 setSalarysetTitle 等开始。它们是 OOP 应该做的可怕的变态,失去了很多可读性,而在封装方面却一无所获。

类不应提供成员函数来操作类的所有成员,而应提供更高级别的成员来从流中重构自身的实例。

当您获得数据时,您可能想为您的 People/Employees/Students 数组创建单独的对象。相反,每个项目都将进入员工或学生的数组。那么People 将只是一个指向其他两个数组中项目的指针数组。

关于读取数据的细节:我个人可能会写一个ctype类,将:分类为空白,然后读取数据。不过,对于您的班级,您可能希望坚持使用getline

class Person { 
    virtual std::istream &read(std::istream &is);

    friend std::istream &operator>>(std::istream &is, Person &p) { 
        return p.read(is);
    }
};

class Student : public Person { 
    std::string first_name;
    std::string last_name;
    std::string age;
    std::string ssn;
    std::string ID;
    std::string title;
    std::string salary;

    virtual std::istream &read(std::istream &is) { 
        std::getline(is, first_name, ':');
        std::getline(is, last_name, ':');
        std::getline(is, age, ':');
        // ...
        return is;
    }
};

有了这些,从文件中读取数据通常会非常简单:

std::string t;
Employee e;
Student s;

while (std::getline(infile, t, ':'))
    if (t == "e") {
        infile >> e;
        Employees.push_back(e);
    }
    else if (t =="s") {
        infile >> s;
        Students.push_back(s);
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-19
    • 2015-09-08
    • 1970-01-01
    相关资源
    最近更新 更多