8

I have a CSV file formatted as below:

1,50,a,46,50,b
2, 20,s,56,30,f
3,35,b,5,67,s
...

How can I turn that to a 2D array so that I could do some calculations?

void Core::parseCSV(){
    std::ifstream  data("test.csv");
    std::string line;
    while(std::getline(data,line))
    {
        std::stringstream lineStream(line);
        std::string cell;
        while(std::getline(lineStream,cell,','))
        {
            //not sure how to create the 2d array here
        }
    }
};
Morgoth
  • 4,935
  • 8
  • 40
  • 66
hmaxx
  • 597
  • 2
  • 8
  • 18
  • DId you look at existing answers to similar questions - e.g. [here](http://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c)? From those, what did you try, and what trouble did you run into? – Tony Delroy Dec 11 '15 at 07:19
  • @TonyD, I have edited the question. – hmaxx Dec 11 '15 at 07:24
  • What do you expect to have happen with values like `a` and `b`? Is there some hard-coded mapping to numeric values you could do calculations with? Or should they be ignored? Do you have to remember them to "solve" some matrix system for their values? If you don't want to deal with those issues initially, you could simply read the values into a `std::vector> values;`, which you'd put before the first `while` loop: inside that loop, `push_back` an empty `vector` or `values.resize(values.size() + 1);` if you prefer, then push back cell values inside. – Tony Delroy Dec 11 '15 at 07:26
  • Basically, What I want is like I have the id 1,2,3 which goes up to to 2k and have that id associated with all the other values. `1: [50,'a'a,46,50,'b'], 2: [2,20,'s',56,30,'f']....` – hmaxx Dec 11 '15 at 07:32
  • Why you ask for a 2D array? I see a 1D array of a data structure (made of 6 fields, 4 numbers and 2 strings). – marom Dec 11 '15 at 07:42
  • @marom, I might be confused but all I want is an array containing all those info for each line. – hmaxx Dec 11 '15 at 07:44

3 Answers3

7

Try this,

void Core::parseCSV()
{
    std::ifstream  data("test.csv");
    std::string line;
    std::vector<std::vector<std::string> > parsedCsv;
    while(std::getline(data,line))
    {
        std::stringstream lineStream(line);
        std::string cell;
        std::vector<std::string> parsedRow;
        while(std::getline(lineStream,cell,','))
        {
            parsedRow.push_back(cell);
        }

        parsedCsv.push_back(parsedRow);
    }
};
Community
  • 1
  • 1
Jain
  • 854
  • 5
  • 15
3

Following code is working. hope it can help you. It has a CSV file istream_iterator-like class. It is a template so that it can read strings, ints, doubles, etc. Its constructors accept a char delimiter, so that it may be used for more than strictly comma-delimited files. It also has a specialization for strings so that white space characters could be retained.

#include <iostream>
#include <sstream>
#include <fstream>
#include <iterator>

using namespace std;

template <class T>
class csv_istream_iterator: public iterator<input_iterator_tag, T>
{
    istream * _input;
    char _delim;
    string _value;
public:
    csv_istream_iterator( char delim = ',' ): _input( 0 ), _delim( delim ) {}
    csv_istream_iterator( istream & in, char delim = ',' ): _input( &in ), _delim( delim ) { ++*this; }

    const T operator *() const {
        istringstream ss( _value ); 
        T value;
        ss >> value;
        return value;
    }

    istream & operator ++() {
        if( !( getline( *_input, _value, _delim ) ) )
        {
            _input = 0;
        }
        return *_input;
    }

    bool operator !=( const csv_istream_iterator & rhs ) const {
        return _input != rhs._input;
    }
};

    template <>
    const string csv_istream_iterator<string>::operator *() const {
        return _value;
    }

    int main( int argc, char * args[] )
    {
        { // test for integers
            ifstream fin( "data.csv" );
            if( fin )
            {
                copy( csv_istream_iterator<int>( fin ),
                      csv_istream_iterator<int>(),
                      ostream_iterator<int>( cout, " " ) );

                fin.close();
            }
        }

        cout << endl << "----" << endl;

        { // test for strings
            ifstream fin( "data.csv" );
            if( fin )
            {
                copy( csv_istream_iterator<string>( fin ),
                      csv_istream_iterator<string>(),
                      ostream_iterator<string>( cout, "|" ) );

                fin.close();
            }
        }

        return 0;
    }
Morgoth
  • 4,935
  • 8
  • 40
  • 66
Confiz-3C
  • 71
  • 2
-1

I would go for something like this (untested, incomplete) and eventually refine operator >>, if you have strings instead of chars, or floats instead of ints.

struct data_t
{
  int a ;
  int b ;
  char c ;
  int d ; 
  int e ;
  char f ;
} ;

std::istream &operator>>(std::istream &ist, data_t &data)
{
    char comma ;
    ist >> data.a >> comma 
        >> data.b >> comma
        >> data.c >> comma
        >> data.d >> comma
        >> data.e >> comma
        >> data.f 
    ;
    return ist ;
}
void Core::parseCSV(){
  std::ifstream  data("test.csv");
  std::string line;
  std::vector<data_t> datavect ;
  while(std::getline(data,line))
  {
    data_t data ;
    std::stringstream lineStream(line);
    lineStream >> data ;
    datavect.push_back(data) ;
  }
};
Morgoth
  • 4,935
  • 8
  • 40
  • 66
marom
  • 5,064
  • 10
  • 14