C++ 如何在C+中读取带有逗号分隔值的流+;?

C++ 如何在C+中读取带有逗号分隔值的流+;?,c++,csv,stream,C++,Csv,Stream,我想强调以下一个基本问题: 假设您有一个CSV文件,并用输入标题填充单元格 代码应该从.csv文件中读取此内容,并将结果写入.csv文件。还可以编写输出案例总数加上案例平均数的代码。这是一个样本表格,我想看看如何有效地采用它来完成这个基本示例 void create() { // file pointer fstream fout; // opens an existing csv file or creates a new file. fout

我想强调以下一个基本问题:

假设您有一个CSV文件,并用输入标题填充单元格

代码应该从.csv文件中读取此内容,并将结果写入.csv文件。还可以编写输出案例总数加上案例平均数的代码。这是一个样本表格,我想看看如何有效地采用它来完成这个基本示例

   void create() 
{ 
    // file pointer 
    fstream fout; 

    // opens an existing csv file or creates a new file. 
    fout.open("reportcard.csv", ios::out | ios::app); 

    cout << "Enter the details of 5 students:"
        << " roll name maths phy chem bio"; 
    << endl; 

    int i, roll, phy, chem, math, bio; 
    string name; 

    // Read the input 
    for (i = 0; i < 5; i++) { 

        cin >> roll 
            >> name 
            >> math 
            >> phy 
            >> chem 
            >> bio; 

        // Insert the data to file 
        fout << roll << ", "
            << name << ", "
            << math << ", "
            << phy << ", "
            << chem << ", "
            << bio 
            << "\n"; 
    } 
} 
void create()
{ 
//文件指针
fstream-fout;
//打开现有csv文件或创建新文件。
fout.open(“reportcard.csv”,ios::out | ios::app);
姓名
>>数学
>>物理层
>>化学
>>生物;
//将数据插入到文件中

fout我主要集中在为
操作符
添加重载,因为您在前面对这些重载表示了一些兴趣,并在代码的注释中描述了它们在做什么

由于您混合了逗号分隔的流和其他流的输入和输出,因此我添加了一个用于
CSV
流的适配器,以及用于向/从用户流的重载

首先,创建一个
,将所有属于同一个数据记录的数据保存在一起。我在这里把它做成了一个简单的
结构
,它是一个
,默认情况下可以访问其成员

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

// your student record
struct student {
    std::string name; // It's usually good to have larger types first so name goes first
    int roll;
    int math;
    int phy;
    int chem;
    int bio;
};

// read a student from an istream (like std::cin) - whitespace separated
std::istream& operator>>(std::istream& is, student& s) {
    return is >> s.roll >> s.name >> s.math >> s.phy >> s.chem >> s.bio;
}

// write a student to an ostream (like std::cout)
std::ostream& operator<<(std::ostream& os, const student& s) {
    return os << "Details of Roll " << s.roll << ":\n"
              << "Name: " << s.name << '\n'
              << "Maths: " << s.math << '\n'
              << "Physics: " << s.phy << '\n'
              << "Chemistry: " << s.chem << '\n'
              << "Biology: " << s.bio << '\n';
}
//--------------------------------------------------------------------------------------
// An adapter for comma separated streaming
struct CSVStudent {
    CSVStudent(student& s) : stud(s) {}
    CSVStudent(const CSVStudent&) = delete;

    // The CSVStudent holds a reference to a "student"
    student& stud;
};

// read a record from an istream - comma separated
std::istream& operator>>(std::istream& is, CSVStudent& csvstud) {
    std::string line;

    student& s = csvstud.stud; // an alias to the student to have to type less

    if(std::getline(is, line)) { // read a complete line
        // put the line in an istringstream for extraction:
        std::istringstream ss(line);

        char delim; // a dummy for reading commas

        // Extract the comma separated values. "delim" is not checked so it could be
        // any char breaking up the int:s.
        //
        // The below does things in the following order:
        // 1.  "ss >> s.roll >> delim"
        //     This extracts roll and a comma and returns
        //     a reference to ss, which is used in 2.
        // 2.  std::getline(ss, s.name, ',')
        //     Extracts a string until a comma is encountered.
        // 3.  Normal extraction for the rest of the int:s with the
        //     dummy variable "delim" where the commas are supposed to be.

        if(not(std::getline(ss >> s.roll >> delim, s.name, ',') >> s.math >> delim >>
               s.phy >> delim >> s.chem >> delim >> s.bio)) {
            // If we get here, the extraction from the istringstream failed, so set
            // the failstate on the istream too. Note the "not" on the line above.
            is.setstate(std::ios::failbit);
        }
    }
    return is;
}

// write a record to an ostream - comma separated
std::ostream& operator<<(std::ostream& os, const CSVStudent& csvstud) {
    const student& s = csvstud.stud;
    os << s.roll << ',' << s.name << ',' << s.math << ',' << s.phy << ',' << s.chem
       << ',' << s.bio << '\n';
    return os;
}
//--------------------------------------------------------------------------------------
// get all students in the file as a std::vector<student>
std::vector<student> read_student_file(const std::string& filename) {
    std::vector<student> retval;
    std::ifstream fin(filename);
    if(fin) { // file opened successfully
        student stud;
        CSVStudent csvstud{stud}; // holds a reference to stud

        // loop for as long as student records can be read successfully
        while(fin >> csvstud)       // use the csv sdapter
            retval.push_back(stud); // and put the stud in the vector
    }
    return retval;
}
//--------------------------------------------------------------------------------------
void create(const std::string& filename) {
    // open an existing csv file or creates a new file.
    std::ofstream fout(filename, std::ios::out | std::ios::app);
    if(fout) {
        std::cout << "Enter the details of 5 students:"
                     " roll name maths phy chem bio\n";

        // Read the input
        for(int i = 0; i < 5; i++) {
            student stud;
            std::cout << (i + 1) << ": ";
            if(std::cin >> stud) {
                // Insert the data to file if one was entered successfully
                fout << CSVStudent(stud); // uses the adapters operator<<
            } else {
                std::cerr << "You failed to enter data for student " << (i + 1) << '\n';
                break;
            }
        }
    }
}
//--------------------------------------------------------------------------------------
int main() {
    std::string filename = "reportcard.csv";

    std::vector<student> students = read_student_file(filename);

    std::cout << "There are " << students.size() << " students in the file.\n";
    if(not students.empty()) {
        // show the last record if there are any records in the file
        std::cout << "Record " << students.size() << " is:\n\n";
        std::cout << students.back() << '\n';
    }

    // create 5 new records
    create(filename);
}
程序应按如下方式启动:

There are 5 students in the file.
Record 5 is:

Details of Roll 5:
Name: Bork
Maths: 5
Physics: 6
Chemistry: 7
Biology: 8

Enter the details of 5 students: roll name maths phy chem bio
1: <and here is where you're supposed to enter the first of 5 new students>
文件中有5名学生。
记录5是:
第5卷的详细信息:
姓名:博克
数学:5
物理:6
化学:7
生物学:8
输入5名学生的详细信息:卷名数学物理化学生物

1:你忘了带标签。这看起来不是很简单(如中所示)而且你也很难理解你所拥有的问题。上面提到的代码中的示例数据似乎与代码无关。代码涉及移动平均值等。添加了标记C++。此外,还添加了一个语句,平均值和案例总数也在愿望列表中。RE有很多例子,问题是如何导航,修改它们,以便对新用户C++进行最基本的练习,就像我在这个问题上提出的那样。你能为这个提议做出贡献吗?恐怕你误解了这个站点应该如何工作。人们通常不会为你做实现/适应。试着自己做,如果你遇到了问题,就提出一个你需要帮助的问题。你有简单的逗号分隔的数据,或者有一个奇特的CSV格式,里面有引号和逗号之类的东西吗?现在更好了,虽然它不是一个。如果没有人能比我更好,我会回答的。:-)一个更好的方法是有一个简单的wrapper类(CVSStudent),它只保存对学生的引用,但当序列化器/反序列化器像CVS文件一样读/写时,而普通学生以人类可读的形式进行流式处理:`std::cin>>CVSStudent{studentObject};std::cout@MartinYork是的,我同意添加一层适配器会使它更通用,并保持它更干净。我可能会再补充一点。@Mohammad将关注点分离是很好的。通过使
学生
简单,并且只能够从任何
ostream/istream
添加基本的流媒体,就很容易添加支持端口类、适配器,以其他特定方式执行流式传输,如
csv
等@Mohammad您需要在适用的地方添加验证器,并根据您认为合适的内容拒绝或接受值。要删除值,您只需从
std::vector
中删除它,然后将向量中的所有学生写入fil即可e来覆盖您以前拥有的内容。可以通过多种方式完成。如果
roll
被认为是唯一的数字,您可以使用
std::map studentmap;
roll
作为键,将
student
作为值来创建一个
student
学生地图。然后可以使用
student&stud=stude>来获得某个学生ntmap.at(roll);
或者您可以保留
向量和
std::在其中查找学生。无限的可能性。:-)
1,Ted,1,2,3,4
2,Foo,2,3,4,5
3,Bar,3,4,5,6
4,Baz,4,5,6,7
5,Bork,5,6,7,8
There are 5 students in the file.
Record 5 is:

Details of Roll 5:
Name: Bork
Maths: 5
Physics: 6
Chemistry: 7
Biology: 8

Enter the details of 5 students: roll name maths phy chem bio
1: <and here is where you're supposed to enter the first of 5 new students>