简体   繁体   中英

Read columns from a comma delimited data file c++

I have been trying to read following data table and create an object for the HUBs(rows) and another object for continent (columns). Since I am not a C++ experienced user I have been facing some difficulties. The data is in following. The number after HUB and the dash shows the order from the hub. The other numbers under each continent are the corresponding cost and tariffs between a HUB and continent. I would like to be able to cout for instance following and get the result which would be 73. cout << hub(1)->cont(USA)->transport() << endl;

,USA,EUROPE,ASIA
HUB1-12000,,,
Transportation Cost,73,129,141
Tariffs,5,5,1
ShippingType,a,b,c
OtherFees,0.6,0.3,0.8
HUB2-11000,,,
Transportation Cost,57,101,57
Tariffs,7,7,5
ShippingType,b,b,d
OtherFees,0.7,0.3,0.6

Really appreciate your help. Here is what I have tried so far:

    void Hub()
   {
     string file = "/hubs.csv";         

     // 1-First read the first line and save the continent name
     string str, field;
     getline( fin, str );
     vector<string> contList;
     stringstream linestr( str );
     while (  linestr.good() )
     {
       getline( linestr, field, ',' );
       string contname;
       contList.push_back(contname);
     }

     // 2-Then read the rest
     getline( fin, str );
     while ( !fin.eof() )  // Read the whole file
     {
       stringstream linestr( str );
       string contname, order;
       if ( qstr[0] == 'HUB1' || qstr[0] == 'HUB2')         
       {
         // Read the name of the hub
         getline( linestr, hubname, ',' );           // Read the hub name
         getline( linestr, order, ',' );             // Read the order quantityity

         int quantity;
         istringstream orderstream( order);
         orderstream >> quantity;

         // Find the hub and add the order to the hub
         Hub* hub = glob->FindHubName( hubname ); // this returns a pointer 
         if ( glob->FindHubName( hubname ) == nullptr )
         {
           hubNotFound.push_back( hubname );
           getline( fin, qstr );
           continue;
         }
         hub->TotalOrder( quantity );
       }
       else if ( qstr[0] != 'HUB1' || qstr[0] != 'HUB2')   
       {
         // Read costs and tariffs 
         cout << hub(1)->cont(ASIA)->transport() 
        }
       getline( fin, qstr );
     }
     fin.close();
   }

Something like this:

#include <iostream>
#include <fstream>
#include <boost/tokenizer.hpp>
#include <string>

int main() {
   using namespace std;
   using namespace boost;

   string line, file_contents;
   fstream file("test.csv");

   if (!file.is_open()) {
     cerr << "Unable to open file" << endl;
     return 1;
   }


   getline(file, line);
   tokenizer<> tok_head(line);
   int n_columns = 0;
   for (tokenizer<>::iterator beg=tok_head.begin(); beg!=tok_head.end(); ++beg) {
       cout << *beg << '\t';
       n_columns++;
   }
   cout << endl;

   while (getline(file, line)) {
     file_contents += line;
   }

   file.close();

   tokenizer<> tok(file_contents);


   int i = 0;
   for (tokenizer<>::iterator beg=tok.begin(); beg!=tok.end(); ++beg, ++i) {
       cout << *beg;
       if (i % n_columns) {
         cout << '\t';
       } else {
         cout << endl;
       }
   }

   return 0;
}

Makefile

all: t

t: csv.cpp
    g++ -I /usr/include/boost csv.cpp -o t

It looks like you must parse each line using different logic, so you should check first column first and using it apply appropriate logic, below is some pseudocode for that:

    std::fstream fs("test.txt");
    std::string line;

    //
    // Read line by line
    while (std::getline(fs, line)) {

        std::istringstream str(line);
        std::string rec_type;

        // Read record type (your first two lines looks like are of no type?)
        if ( !std::getline(str, rec_type, ',') )
            continue;

        // Decide type of record, and parse it accordingly
        if ( rec_type == "Transportation Cost") {
            std::string val;

            // Read comma delimited values
            if ( !std::getline(str, val, ',') )
                continue;
            int ival1 = std::stoi(val);

            if ( !std::getline(str, val, ',') )
                continue;
            int ival2 = std::stoi(val);
            // ...
        }

        if ( rec_type == "Tariffs") {
            std::string val;
            if ( !std::getline(str, val, ',') )
                continue;
            int ival = std::stoi(val);
            // ...
        }
    }

One method is to consider each line as a separate record and object.
Let the objects read their data.

For example:

class Tariff
{
  int values[3];
  public:
    friend std::istream& operator>>(std::istream& input, Tariff& t);
};

std::istream& operator>>(std::istream& input, Tariff& t)
{
  // Read and ignore the label "Tariff"
  std::string name;
  std::getline(input, name, ','); // Read until ',' delimiter.
  input >> t.value[0];
  // Note: the ',' is not a digit, so it causes an error state,
  // which must be cleared.
  input.clear();
  input >> t.value[1];
  input.clear();
  input >> t.value[2];
  input.clear();
}  

Another method is to read the label first, then delegate to a function that reads in the row.

std::string row_text;
std::getline(text_file, row_text);  // Read in first line and ignore.
while (std::getline(text_file, row_text))
{
  std::istringstream text_stream(row_text);
  std::string label;
  std::getline(text_stream, label, ','); // Parse the label.

  // Delegate based on label.
  // Note: can't use switch for strings.
  if (label == "Tariffs")
  {
      Input_Tariff_Data(text_stream);
  }
  else if (label == "ShippingType")
  {
      Input_Shipping_Type_Data(text_stream);
  }
  //...
} // End-while 

The if-else ladder can be replaced by a lookup table that uses function pointers. Sometimes the table is easier to read.

typedef void (*P_Input_Processor)(std::istringstream& text_stream);
struct Table_Entry
{
  char const * label;
  *P_Input_Processor input_processor;
};

//...
const Table_Entry delegation_table[] =
{
  {"Tariffs", Input_Tariff_Data},
  {"ShippingType", Input_Shipping_Type_Data},
};
const unsigned int entry_quantity =
    sizeof(delegation_table) / sizeof(delegation_table[0]);
// ...
std::string row_text;
std::getline(input_file, row_text); // Read and ignore first line.
while (std::getline(input_file, row_text))
{
  // Create a stream for parsing.
  std::istringstream text_stream(row_text);

  // Extract label text
  std::string label;
  std::getline(text_stream, label, ',');

  // Lookup label in table and execute associated function.
  for (unsigned int index = 0; index < entry_quantity; ++index)
  {
    if (label == delegation_table[index].name)
    {
      // Execute the associated input function
      // by derferencing the function pointer.
      delegation_table[index](text_stream);
      break;
    }
  }
}

An alternative to the lookup table is to use:
std::map<std::string, P_Input_Processor>
or
std::map<std::string, void (*P_Input_Processor)(std::istringstream&)>

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