Search code examples
c++stringvectorstleclipse-cdt

Facing problem with iterating over vector<vector<string>> and printing data


I am reading a CSV file and storing it in vector vector string. I want to print the data and for that I am using two for loops one iterating over vector of vector and other iterating over the vector string.

a1,b1,c1,d1
a1,b1,c4,d3
a1,b2,c2,d2
a2,b3,c3,d4
a2,b4,c3,d4

this is the CSV data I am reading. Below code I am using to print it to screen

void ReadCSV::printdata(vector<vector<string>> ipd){
    for(auto it1 = ipd.begin();it1 != ipd.end();++it1){
        vector<string> test = *it1;
        for(auto it2 = test.begin();it2 != test.end();++it2){
            string r = "";
            r= *it2;
            cout<<r<<" ";
        }
        cout<<endl;
    }
}

But the output I am getting seems is not iterating properly:

a1 b1 c1 d1 
a1 b1 c1 d1 a1 b1 c4 d3 
a1 b1 c1 d1 a1 b1 c4 d3 a1 b2 c2 d2 
a1 b1 c1 d1 a1 b1 c4 d3 a1 b2 c2 d2 a2 b3 c3 d4 
a1 b1 c1 d1 a1 b1 c4 d3 a1 b2 c2 d2 a2 b3 c3 d4 a2 b4 c3 d4

I used below code to read data:

vector<vector<string>> ReadCSV:: ReadData(){
    fstream fin(filename);
    vector<string> temp;
    string val1, val2, val3 ,val4;
    if(!fin.is_open()){
        cout<<"ERROR: file open";
    }
    cout<<"FIRST OUTPUT: "<<endl;
    while(fin.good()){
        getline(fin, val1,',');
        //store
        temp.push_back(val1);
        cout<<val1<<" ";
        getline(fin, val2,',');
        temp.push_back(val2);
        cout<<val2<<" ";
        getline(fin, val3,',');
        temp.push_back(val3);
        cout<<val3<<" ";
        getline(fin, val4,'\n');
        temp.push_back(val4);
        cout<<val4<<" ";
        csvdata.push_back(temp);
    }
    cout<<endl;
    return csvdata;
}

Can anyone tell where I am going wrong, other issue I face is when I run debugger (ECLIPSE IDE) and hover over a variable it opens up some popup but doesn't displays the value of the variable, such as "string r" in this case. thanks


Solution

  • Your CSV reader besides having some minor issues is hard-coded to read exactly 4 comma separated values. Here is a more general code which can read as many lines and with each line having variable number of comma separated values.

    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <iterator>
    #include <algorithm>
    
    template<typename In, typename Out>
    void csv2vec(In first, In last, Out& result) {
        std::string temp;
        unsigned i{0};
        while(1) {
            do{
                if(*first == '\n') break;
                temp.push_back(*first);
                ++first;
            }while(*first != ',' && first != last);
            result[i].push_back(temp);
            if(*first == '\n') {
                ++i;
                result.resize(i+1);
            }
            temp.clear();
            if(first == last) break;
            ++first;
        }
    }
    
    int main(void) {
        std::vector<std::vector<std::string>> vec(1);
        std::ifstream in("data.txt");
        std::istreambuf_iterator<char> begin(in);
        std::istreambuf_iterator<char> end;
        csv2vec(begin,end,vec);
    
        for(const auto& vecouter : vec) {
            for(const auto& elem : vecouter){
                std::cout<<elem<<" ";
            }
            std::cout<<"\n";
        }
       return 0;
    }
    

    Note: I have used istreambuf_iterator which never skips any characters (including whitespaces such as '\n'). It grabs whatever's next in the stream buffer unlike istream_iterator which skips whitespaces. Also istream_iterator is useful when performing formatted input and is slower than the istreambuf_iterator. Reference: Item 29, Effective STL, Scott Meyers.