-3

I have a .csv file with integers in two columns and many rows. It looks like this:

    A      B
1  584    146
2  586    167
.   .      .
.   .      .

I want to create a 2D array of integers from this .csv file in C++. I've searched dozens of websites but all of the answers I could get was with strings.

This is how the raw data looks like:

584,146
586,167
588,189
Carrot Cake
  • 57
  • 2
  • 8

2 Answers2

3

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.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
0

You should start with reading each line with std::getline, store the line in a std::stringstream, then read the individual tokens using the overloaded std::getline that takes a delimiter.

You can then parse these tokens to integers using std::stoi, and store them in a std::vector< std::pair<int, int> >.

A good explanation with a working code sample can be found here.

CK.
  • 312
  • 3
  • 13