简体   繁体   中英

Read 2D array in CSV into a Map c++

I am a CPLEX user and I need to convert array data in a CSV file to a C++ 2D Map as input to my optimization problem. The data is present as columns. That is, if we consider 3 rows (AA1, AA2, AA3) and 3 columns (BB1, BB2, BB3) and their respective values, they have the following format in the CSV file:

ROW, COL, VALUE
AA1, BB1, 0.3
AA2, BB1, 0.5
AA1, BB2, 0.6
AA1, BB3, 0.7
AA2, BB2, 0.9
AA3, BB2, 0.5
AA3, BB1, 0.6
AA2, BB3, 0.4
AA3, BB3, 0.6

As you can see, the data is not sorted either by rows or columns. I hope to read the data into a 2D Map of the format " map< string, map< string, float >> " such that the resulting map is ordered:

ROW, COL, VALUE
AA1, BB1, 0.3
AA1, BB2, 0.6
AA1, BB3, 0.7
AA2, BB1, 0.5
AA2, BB2, 0.9
AA2, BB3, 0.4
AA3, BB1, 0.6
AA3, BB2, 0.5
AA3, BB3, 0.6

Any help will be greatly appreciated! Thank you.

[EDIT] The number of rows and columns are known in advance - no need to count. I am relatively new to C++ (migrating from the OPL optimization language) and I am not very familiar with file operations. Any sample code will be greatly helpful for me to understand the procedure.

Since you are using whitespace as your delimiting character, you should be able to implement this rather quickly using the standard <ifstream> implementation:

#include <ifstream>
#include <string>
#include <map>
#include <iostream>

int main() {
    // Open file:
    std::ifstream fin("filename.csv"); // Open in text-mode.

    // Opening may fail, always check.
    if (!fin) { 
        std::cout << "Error, could not open file." << std::endl; 
        return -1;
    }

    // Construct your map.
    std::map< std::string, std::map<std::sting, float> > valMap;

    // Read the file and load the data:
    std::string row, column;
    float value;
    while ( fin >> row >> column >> value ) { // Reads three values from file.
          valMap[row][col] = value;  // Adding the values to the map, if the cell is 
                                     // a duplicate: It will be overwritten.
    }

    // Close the file:
    fin.close();

    // Printing out the data like you described:
    for ( auto & columnMap : valMap ) {
        for ( auto cell : columnMap.second ) {
                std::cout << columnMap->first /*First label*/ << " "
                          << cell->first      /*Second label*/ << " "
                          << cell->second     /* Value */ << std::endl;
        }
    }

    return 0;
}

Ok, I'll add this in a second answer as it is a completely different approach to the same problem.

If you can have any number of different delimiters, strtok is an excellent option. It is originally C, so you will need to get your hands a little more dirtly, but it's worth it.

char * strtok ( char * str, const char * delimiters );

Takes two parameters, the first one is the string we are to analyse, the second is a string containing all the different characters we want to use as delimiters.

For your particullar use case I would suggest something along the following:

#include <cstring>
#include <cstdlib>
#include <string>
#include <fstream>
#include <iostream>

const char * DELIMS = "\t ,"; // Tab, space or comma.
const int MAX_LINE_LENGTH = 1024; // Choose this large enough for your need.
                                  // Or use a dynamic buffer together with 
                                  // std::string and getline(istream, string&).

int main() {
    std::fstream fin("filename.csv");

    // Test for errors

    // Prepare a C-string buffer to be used when reading lines.
    char buffer[MAX_LINE_LENGTH] = {};        

    // Prepare map.
    std::map< std::string, std::map< std::string, float>> valMap;

    // Read one line at a time.
    while ( fin.getline(buffer, MAX_LINE_LENGTH) ) {
          // Extract the three tokens:
          const char * row = strtok( buffer, DELIMS );
          const char * col = strtok( NULL,   DELIMS );
          const char * val = strtok( NULL,   DELIMS );

          // You could do additional errorchecking here, 
          // as for instance checking that there are no more valid tokens, 
          // and that all row, col and val are non-NULL.

          // Insert elements.
          valMap[std::string(row)][std::string(col)] = std::atof(val);
    }      

    // Cleanup
    fin.close();
}  

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM