简体   繁体   中英

read a csv file and and add its all data into vector in c++

For example to add the following CSV data:

在此处输入图片说明

I am trying to add CSV file into a 2D array string vector and get the sum of each column. The following program didn't work properly,

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;
}

when I tried to go through the vector, I didn't get a proper result. It showed only the column names.

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

I couldn't find whether the error is in read_csv() function or in the forloop. Thank you for looking at this problem.

In your while loop, you never pushed any values to your vector.

It looks like you have everything you need to read the csv into a vector right here. Only problem is you stopped at column names.

// 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;
        }
    }

Try changing the code I copied above to:

// 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;
        }
    }
  1. Don't try to create vectors of std::string s, that's probably not very efficient - each string being allocated and de-allocated separately.
  2. Don't read CSV's yourself - you're reinventing the wheel. Use an existing library. Here's a question about finding one at Software Recommendations StackExchange:

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

I cannot believe that we are using a library for such an ultra simple thing like splitting a std::string into tokens.

C++ has, since long, a build in and dedicated functionality, specifically designed for this purpose, to tokenize strings (split strings into tokens). And because such a simple dedicated function, designed for this purpose, is available, it simply should be used. There is no need for external libraries or complicated constructs. Simply use the std::sregex_token_iterator .

This is an iterator (like many other iterators), that iterates over tokens (sub-strings) of a string. So, what we want.

We can then use the std::vector s range constructor to write something simple like this:

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

So, we define a variable with the name "tokens" of type std::vector (with CTAD the type of the vector is automatically deduced). We use its range constructor and provide a begin and an end iterator. The begin iterator is the std::sregex_token_iterator and the end-iterator is its default-initialized counterpart.

To put such a vector into a 2D Vector, we use the outer vectors emplace_back function and do an inplace construction for the inner vector.

So you read the whole CSV-File with 2 statements

  • a simple for loop
  • a simple emplace back with the 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), {}));
        }

So, why should you use a library for such a simple task that you can do with 2 statements? I personally fail to understand that. Therefore, I find that the advise in the accepted answer is flat wrong. But, to avoid starting religious discussions: This is my very personal humble opinion and everybody can do what he wants.

Please see a complete working example, which solves your problem, with just a few lines of code . . .

#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;
}

But as said, everybdoy can do whatever he wants.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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