【问题标题】:read a csv file and and add its all data into vector in c++读取一个csv文件并将其所有数据添加到C++中的向量中
【发布时间】:2020-02-20 14:43:53
【问题描述】:

例如添加以下 CSV 数据:

我正在尝试将 CSV 文件添加到二维数组字符串向量中并获取每列的总和。以下程序无法正常运行,

vector<string> read_csv(string filename){

    vector<string> result;
    fstream fin;
    fin.open(filename, ios::in);

    if(!fin.is_open())
        throw std::runtime_error("Could not open file");

    std::string line, colname;
    int val;

    // Read the column names
    if(fin.good())
    {
        std::getline(fin, line);
        std::stringstream ss(line);
        while(std::getline(ss, colname, ',')){
            result.push_back(colname);
            cout << colname << endl;
        }
    }

    while(std::getline(fin, line))
    {
        std::stringstream ss(line);
        int colIdx = 0;
        while(ss >> val){

            if(ss.peek() == ',') ss.ignore();
            colIdx++;
        }
    }
    fin.close();
    return result;
}

当我试图通过向量时,我没有得到正确的结果。它只显示列名。

for (int i = 0; i < vectorCsv.size(); ++i) 
{
        cout << vectorCsv[i] << endl;
}

我找不到错误是在 read_csv() 函数中还是在 forloop 中。 感谢您查看此问题。

【问题讨论】:

  • 我强烈推荐使用库,在 C++ 中解析字符串(和文件)非常繁琐
  • @const_ref 库很棒,但我想这个人正处于学习阶段(从代码和问题来看),所以在我看来,这是一个有益的练习
  • 你在返回的向量中真正添加了什么?
  • 我找不到错误是在 read_csv() 函数中还是在 forloop 中 -- 你使用的是调试器吗?找出错误在哪里是您应该能够做的——修复错误是另一回事。
  • 还要记住你尝试读取整数值,但是你的第二次读取循环只会读取日期和时间的第一个数字,然后ss &gt;&gt; val 会失败。

标签: c++ c++14


【解决方案1】:
  1. 不要尝试创建 std::strings 的向量,这可能效率不高 - 每个字符串都是单独分配和取消分配的。
  2. 不要自己阅读 CSV - 您正在重新发明轮子。使用现有库。下面是一个关于在 Software Recommendations StackExchange 上查找的问题:

    Modern C++ CSV reader (and maybe writer) library

【讨论】:

  • RapidScv 库运行良好。谢谢@einpoklum
【解决方案2】:

在您的 while 循环中,您从未将任何值推送到您的向量。

您似乎拥有将 csv 读入向量所需的一切。唯一的问题是你停在列名上。

// Read the column names
    if(fin.good())
    {
        std::getline(fin, line);
        std::stringstream ss(line);
        while(std::getline(ss, colname, ',')){
            result.push_back(colname);
            cout << colname << endl;
        }
    }

尝试将我上面复制的代码更改为:

// Read the column names
    while(std::getline(fin, line))
    {
        std::getline(fin, line);
        std::stringstream ss(line);
        while(std::getline(ss, colname, ',')){
            result.push_back(colname);
            cout << colname << endl;
        }
    }

【讨论】:

  • 我使用了 result.at(colIdx).push_back(val);但这没有用。
  • 您是否尝试将 CSV 加载到二维数组中?向量的向量(即每列的单独向量)
  • 保留列名是否重要(最好的解决方案取决于它)?如果您只想对列求和,您可以使用第一个循环来获取所有列名并计算有多少列(我们将此变量称为 N)。然后你可以创建一个包含 N 个向量的向量。然后您可以为 N 次迭代执行一个 for 循环,该循环执行 std::getline(ss, colname, ',') 并将结果推送到第 i 个向量。有意义吗?
【解决方案3】:

我不敢相信我们正在使用一个库来完成如此简单的事情,比如将 std::string 拆分为令牌。

长期以来,C++ 都有专门为此目的而设计的内置专用功能,用于对字符串进行标记(将字符串拆分为标记)。并且由于可以使用为此目的而设计的这种简单的专用功能,因此应该使用它。不需要外部库或复杂的构造。只需使用std::sregex_token_iterator

这是一个迭代器(与许多其他迭代器一样),它迭代字符串的标记(子字符串)。所以,我们想要什么。

然后我们可以使用std::vectors 范围构造函数来编写如下简单的东西:

std::vector tokens(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {}));

因此,我们定义了一个名为“tokens”的变量,其类型为std::vector(使用CTAD 会自动推断出向量的类型)。我们使用它的范围构造函数并提供一个开始和结束迭代器。开始迭代器是std::sregex_token_iterator,结束迭代器是其默认初始化的对应项。

要将这样的向量放入二维向量中,我们使用外部向量emplace_back 函数并对内部向量进行就地构造。

所以你用 2 个语句阅读了整个 CSV 文件

  • 一个简单的 for 循环
  • 带有std::sregex_token_iterator 的简单位置
        // We will read all lines of the source file with a simple for loop and std::getline
        for (std::string line{}; std::getline(csvFile, line); ) {

            // We will split the one big string into tokens (sub-strings) and add it to our 2D array
            csvData.emplace_back(std::vector<std::string>(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {}));
        }

那么,为什么要使用库来完成这样一个只需 2 条语句就能完成的简单任务呢?我个人无法理解这一点。因此,我发现接受的答案中的建议是完全错误的。但是,为了避免引发宗教讨论:这是我非常个人的拙见,每个人都可以为所欲为。

请查看一个完整的工作示例,只需几行代码即可解决您的问题。 . .

#include <iostream>
#include <fstream>
#include <vector>
#include <regex>

const std::string csvFileName{ "r:\\csv.csv" };
const std::regex delimiter{ "," };

int main() {

    // Open the file and check, if it could be opened
    if (std::ifstream csvFile(csvFileName); csvFile) {

        // This is our "2D array string vector" as described in your post
        std::vector<std::vector<std::string>> csvData{};


        // Read the complete CSV FIle into a 2D vector ----------------------------------------------------
        // We will read all lines of the source file with a simple for loop and std::getline
        for (std::string line{}; std::getline(csvFile, line); ) {

            // We will split the one big string into tokens (sub-strings) and add it to our 2D array
            csvData.emplace_back(std::vector<std::string>(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {}));
        }
        // -------------------------------------------------------------------------------------------------


        // This is for summing up values
        double DP{}, Dta{}, Dts{};

        // Iterate in a simple for loop through all elements of the 2D vector, convert the vlaues to double and sum them up
        for (size_t i = 1U; i < csvData.size(); ++i) {

            DP += std::stod(csvData[i].at(1));
            Dta += std::stod(csvData[i].at(2));
            Dts += std::stod(csvData[i].at(3));
        }

        // Sho the result to the user
        std::cout << "\nSums:  DP: " << DP << "  Dta: " << Dta << "  Dts: " << Dts << "\n";
    }
    else { // In case that we could not open the source file
        std::cerr << "\n*** Error. Could not open file " << csvFileName << "\n\n";
    }
    return 0;
}

但正如所说,每个人都可以为所欲为。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 2020-07-28
    • 2021-05-08
    • 1970-01-01
    • 1970-01-01
    • 2014-11-13
    • 1970-01-01
    相关资源
    最近更新 更多