繁体   English   中英

如何将 CSV 数据读取到 C++ 中结构向量的指针?

[英]How to read CSV data to pointers of struct vector in C++?

我想在 cpp 中将 csv 数据读取到结构向量,这是我写的,我想将虹膜数据集存储在结构向量 csv std::vector<Csv> *csv = new std::vector<Csv>;的指针中std::vector<Csv> *csv = new std::vector<Csv>;

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

struct Csv{
    float a;
    float b;
    float c;
    float d;
    std::string e;
};

int main(){
    std::string colname;
    
    // Iris csv dataset downloaded from
    // https://gist.github.com/curran/a08a1080b88344b0c8a7
    std::ifstream *myFile = new std::ifstream("iris.csv");
    

    std::vector<Csv> *csv = new std::vector<Csv>;
    
    std::string line;
    
    // Read the column names
    if(myFile->good())
    {
        // Extract the first line in the file
        std::getline(*myFile, line);

        // Create a stringstream from line
        std::stringstream ss(line);

        // Extract each column name
        while(std::getline(ss, colname, ',')){
            
            std::cout<<colname<<std::endl;
            }
    }
    

   // Read data, line by line
    while(std::getline(*myFile, line))
    {
        // Create a stringstream of the current line
        std::stringstream ss(line);

        
    }
        
    return 0;
}

我不知道如何实现这部分代码,它输出带有浮点数和字符串的行。

   // Read data, line by line
    while(std::getline(*myFile, line))
    {
        // Create a stringstream of the current line
        std::stringstream ss(line);

        
    }

进化

我们从您的程序开始,并以您当前的程序风格完成它。 然后我们分析您的代码并将其重构为更具 C++ 风格的解决方案。 最后,我们展示了一个使用更多 OO 方法的现代 C++ 解决方案。

首先你完成的代码:

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

struct Csv {
    float a;
    float b;
    float c;
    float d;
    std::string e;
};

int main() {
    std::string colname;

    // Iris csv dataset downloaded from
    // https://gist.github.com/curran/a08a1080b88344b0c8a7
    std::ifstream* myFile = new std::ifstream("r:\\iris.csv");


    std::vector<Csv>* csv = new std::vector<Csv>;

    std::string line;

    // Read the column names
    if (myFile->good())
    {
        // Extract the first line in the file
        std::getline(*myFile, line);

        // Create a stringstream from line
        std::stringstream ss(line);

        // Extract each column name
        while (std::getline(ss, colname, ',')) {

            std::cout << colname << std::endl;
        }
    }


    // Read data, line by line
    while (std::getline(*myFile, line))
    {
        // Create a stringstream of the current line
        std::stringstream ss(line);
        // Extract each column 
        std::string column;
        std::vector<std::string> columns{};

        while (std::getline(ss, column, ',')) {
            columns.push_back(column);
        }
        // Convert
        Csv csvTemp{};
        csvTemp.a = std::stod(columns[0]);
        csvTemp.b = std::stod(columns[1]);
        csvTemp.c = std::stod(columns[2]);
        csvTemp.d = std::stod(columns[3]);
        csvTemp.e = columns[4];
        // STore new row data
        csv->push_back(csvTemp);
    }
    // Show everything
    for (const Csv& row : *csv)
        std::cout << row.a << '\t' << row.b << '\t' << row.c << '\t' << row.d << '\t' << row.e << '\n';


    return 0;
}

关于从 Csv 文件中读取列的问题,可以这样回答:

你需要一个临时向量。 然后使用std::getline function 来拆分std::istringstream中的数据并将生成的子字符串复制到向量中。 之后,我们使用字符串转换函数并将结果分配到临时 Csv 结构变量中。 完成所有转换后,我们将临时数据移动到生成的 csv 向量中,该向量包含所有行数据。


方案分析。

首先,也是最重要的,在 C++ 中,我们不对拥有的 memory 使用原始指针。 在大多数情况下,我们甚至不应该使用new 如果有的话,应该使用std::unique_ptrstd::make_unique

但是我们根本不需要堆上的动态 memory 分配。 您可以简单地在函数堆栈上定义std::vector 与您的std::string colname;行相同您还可以将std::vectorstd::ifstream定义为普通的局部变量。 例如std::vector<Csv> csv{}; . 只是,如果您将此变量传递给另一个 function,则使用指针,但使用智能指针。

接下来,如果你打开一个文件,比如在std::ifstream myFile("r:\\iris.csv"); 您不需要使用if (myFile->good())测试文件流条件。 std::fstream的 bool 运算符被覆盖,为您提供准确的信息。 请看这里

现在,接下来也是最重要的。

您的源文件的结构是众所周知的。 有一个 header 有 5 个元素,然后是 4 个双精度数,然后结束一个没有空格的字符串。 这让生活变得非常轻松。

如果我们需要验证输入或者字符串中是否有空格,那么我们需要实现其他方法。 但是有了这个结构,我们就可以使用内置的 iostream 设施。 片段

        // Read all data
        Csv tmp{};
        char comma;
        while (myFile >> tmp.a >> comma >> tmp.b >> comma >> tmp.c >> comma >> tmp.d >> comma >> tmp.e)
            csv.push_back(std::move(tmp));

会成功的。 很简单。

因此,重构后的解决方案可能如下所示:

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

struct Csv {
    float a;
    float b;
    float c;
    float d;
    std::string e;
};

int main() {

    std::vector<Csv> csv{};
    std::ifstream myFile("r:\\iris.csv");
    if (myFile) {
        
        if (std::string header{}; std::getline(myFile, header)) std::cout << header << '\n';

        // Read all data
        Csv tmp{};
        char comma;
        while (myFile >> tmp.a >> comma >> tmp.b >> comma >> tmp.c >> comma >> tmp.d >> comma >> tmp.e)
            csv.push_back(std::move(tmp));

        // Show everything
        for (const Csv& row : csv)
            std::cout << row.a << '\t' << row.b << '\t' << row.c << '\t' << row.d << '\t' << row.e << '\n';
    }
    return 0;
}

这已经更加紧凑了。 但还有更多。 . .


在下一步中,我们要添加更多面向 Object 的方法。

关键是对这些数据进行操作的数据和方法应该封装在一个 Object / class / 结构中。 只有 Csv 结构应该知道如何读取和写入其数据。

因此,我们覆盖了 Csv 结构的提取器和插入器运算符。 我们使用与以前相同的方法。 我们只是将读写封装在struct Csv中。

之后,主要的function会更加紧凑,使用更加合乎逻辑。

现在我们有:

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

struct Csv {
    float a;
    float b;
    float c;
    float d;
    std::string e;

    friend std::istream& operator >> (std::istream& is, Csv& c) {
        char comma;
        return is >> c.a >> comma >> c.b >> comma >> c.c >> comma >> c.d >> comma >> c.e;
    }

    friend std::ostream& operator << (std::ostream& os, const Csv& c) {
        return os << c.a << '\t' << c.b << '\t' << c.c << '\t' << c.d << '\t' << c.e << '\n';
    }
};

int main() {

    std::vector<Csv> csv{};
    if (std::ifstream myFileStream("r:\\iris.csv"); myFileStream) {

        if (std::string header{}; std::getline(myFileStream, header)) std::cout << header << '\n';

        // Read all data
        Csv tmp{};
        while (myFileStream >> tmp)
            csv.push_back(std::move(tmp));

        // Show everything
        for (const Csv& row : csv)
            std::cout << row;
    }
    return 0;
}

好的。 已经相当不错了。 比特还有更多可能。


我们可以看到源数据有一个 header 和 Csv 数据。

这也可以建模成一个结构。 我们称之为鸢尾花。 我们还添加了一个提取器和插入器覆盖来封装所有 IO 操作。

此外,我们现在使用现代算法、正则表达式和 IO 迭代器。 我不确定,如果现在这太复杂了。 如果你有兴趣,那么我可以给你更多的信息。 但是现在,我将只向您展示代码。

#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <regex>
#include <iterator>

const std::regex re{ "," };

struct Csv {
    float a;
    float b;
    float c;
    float d;
    std::string e;
    // Overwrite extratcor for simple reading of data
    friend std::istream& operator >> (std::istream& is, Csv& c) {
        char comma;
        return is >> c.a >> comma >> c.b >> comma >> c.c >> comma >> c.d >> comma >> c.e;
    }
    // Ultra simple inserter
    friend std::ostream& operator << (std::ostream& os, const Csv& c) {
        return os << c.a << "\t\t" << c.b << "\t\t" << c.c << "\t\t" << c.d << "\t\t" << c.e << '\n';
    }
};

struct Iris {
    // Iris data consits of header and then Csv Data
    std::vector<std::string> header{};
    std::vector<Csv> csv{};

    // Overwrite extractor for generic reading from streams
    friend std::istream& operator >> (std::istream& is, Iris& i) {
        // First read header values;
        if (std::string line{}; std::getline(is, line)) 
            std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {}, std::back_inserter(i.header));
        
        // Read all csv data
        std::copy(std::istream_iterator<Csv>(is), {}, std::back_inserter(i.csv));
        return is;
    }

    // Simple output. Copy data to stream os
    friend std::ostream& operator << (std::ostream& os, const Iris& i) {
        std::copy(i.header.begin(), i.header.end(), std::ostream_iterator<std::string>(os, "\t")); std::cout << '\n';
        std::copy(i.csv.begin(), i.csv.end(), std::ostream_iterator<Csv>(os));
        return os;
    }
};


// Driver Code
int main() {

    if (std::ifstream myFileStream("r:\\iris.csv"); myFileStream) {

        Iris iris{};

        // Read all data
        myFileStream >> iris;

        // SHow result 
        std::cout << iris;
    }
    return 0;
}

看看主要的 function 和它是多么容易。

如果您有任何疑问,请询问。


语言:C++17

使用 MS Visual Studio 2019 社区版编译和测试

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM