简体   繁体   English

从逗号分隔的数据文件 C++ 中读取列

[英]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).我一直在尝试阅读以下数据表并为 HUB(行)创建一个对象,为大陆(列)创建另一个对象。 Since I am not a C++ experienced user I have been facing some difficulties.由于我不是 C++ 经验丰富的用户,因此我一直面临一些困难。 The data is in following.数据如下。 The number after HUB and the dash shows the order from the hub. HUB 和破折号后面的数字表示从集线器开始的顺序。 The other numbers under each continent are the corresponding cost and tariffs between a HUB and continent.每个大洲下的其他数字是 HUB 和大洲之间的相应成本和资费。 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;我希望能够 cout 例如跟随并得到 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. if-else梯形图可以由使用函数指针的查找表代替。 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&)>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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