The easy way to approach the problem is to recognize that you can read the entire line, and then iterate over the line as a stringstream
using getline
with a ','
as the delimiter to separate the values.
(stringstream
is required, you can't simply separate the values while reading the line itself with getline
-- there would be no obvious end of read at the end of the line -- you would simply read the next value beginning on the next line)
However, if you read the line first, create a stream, and then parse the stream, the read stops at the end of the stream providing you a way to detect the last value in a row, e.g. using a vector to store the values converted to int
with std::stoi
, you could fill each row vector as follows:
std::string line, val; /* string for line & value */
...
while (std::getline (f, line)) { /* read each line */
std::vector<int> v; /* row vector v */
std::stringstream s (line); /* stringstream line */
while (getline (s, val, ',')) /* get each value (',' delimited) */
v.push_back (std::stoi (val)); /* add to row vector */
For your "2D" array use, you actually want a vector
of row vectors
or a std::vector<std::vector<int>>
. So declaring your array as:
std::vector<std::vector<int>> array; /* vector of vector<int> */
you can complete your read of row into the array simply by pushing each row vector v
back into your array
, e.g. (with declarations and loop in context)
std::string line, val; /* string for line & value */
std::vector<std::vector<int>> array; /* vector of vector<int> */
while (std::getline (f, line)) { /* read each line */
std::vector<int> v; /* row vector v */
std::stringstream s (line); /* stringstream line */
while (getline (s, val, ',')) /* get each value (',' delimited) */
v.push_back (std::stoi (val)); /* add to row vector */
array.push_back (v); /* add row vector to array */
}
All that remains is accessing each value so you can make use of them. The range-based loop provides just what you need (actually a nested pair of range based loops, one to iterate over the vectors, the second to iterate over values. For example, you could do:
for (auto& row : array) { /* iterate over rows */
for (auto& val : row) /* iterate over vals */
std::cout << val << " "; /* output value */
std::cout << "\n"; /* tidy up with '\n' */
}
Putting all the pieces of the puzzle together, you could do something similar to:
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
int main (int argc, char **argv) {
std::ifstream f;
if (argc > 1) { /* if argument given */
f.open (argv[1]); /* open file with filename as argument */
if (! f.is_open()) { /* validate file open for reading */
std::cerr << "error: file open failed '" << argv[1] << "'.\n";
return 1;
}
}
else { /* no argument given, error show usage */
std::cerr << "error: insufficient input. <filename> required.\n";
return 1;
}
std::string line, val; /* string for line & value */
std::vector<std::vector<int>> array; /* vector of vector<int> */
while (std::getline (f, line)) { /* read each line */
std::vector<int> v; /* row vector v */
std::stringstream s (line); /* stringstream line */
while (getline (s, val, ',')) /* get each value (',' delimited) */
v.push_back (std::stoi (val)); /* add to row vector */
array.push_back (v); /* add row vector to array */
}
for (auto& row : array) { /* iterate over rows */
for (auto& val : row) /* iterate over vals */
std::cout << val << " "; /* output value */
std::cout << "\n"; /* tidy up with '\n' */
}
}
Example Input File
Which given your input file of:
$ cat dat/nums.csv
584,146
586,167
588,189
Example Use/Output
It would be parsed and stored as a vector of integer vectors (providing plain-old 2D array access).
$ /bin/vect2d_strstream dat/nums.csv
584 146
586 167
588 189
Look things over and let me know if you still have questions.