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;
}
}
std::string
s, that's probably not very efficient - each string being allocated and de-allocated separately.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:
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
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.