被问到在用C++处理.csv文件时,每行有多种类型的数据,如果在首行可以读取到不同列的类型,接下来如何读取剩下的数据?
问题在于C++不是动态类型的语言,如何在运行时确定存储类型,参考知乎上的一些回答,了解到直接可以使用C++17支持的std::any/std::variant来进行对未知类型的存储。其它的一些方法基本会通过union来实现一个结构支持不同的类型。
我试验了使用any的情况,发现基于any的实现可以很容易的存入容器比如vector<std::any>,但是当需要取出来时还是需要去对所有类型进行一个判断比较,使用std::any_cast去得到对应的类型。
xxxxxxxxxx3412345using namespace std;67int main()8{9 vector<any> anys;10 int a = 10;11 double b = 2.5;12 char c = 'f';13 string d = "Hello";1415 anys.emplace_back(a);16 anys.emplace_back(b);17 anys.emplace_back(c);18 anys.emplace_back(d);1920 for (const auto& t : anys) {21 if (t.type() == typeid(int)) {22 cout << "int:" << any_cast<int>(t) << endl;23 }24 else if (t.type() == typeid(double)) {25 cout << "double:" << any_cast<double>(t) << endl;26 }27 else if (t.type() == typeid(char)) {28 cout << "char:" << any_cast<char>(t) << endl;29 }30 else {31 cout << "string:" << any_cast<const string&>(t) << endl;32 }33 }34 return 0;35}再就是看到梁笔记博客上的实现方法,贴过来。
定义一个结构AnyData,结构中定义状态Eflag(用于判断是存的是什么类型)和union(里面存数据)。
结构AnyData定义构造函数,为每种类型定义一个。
getType( )用于获取当前数据类型。
getVal( )重载函数用于获取实际数据值。
用STL vector容器为例,实现存放不同类型完整代码:
xxxxxxxxxx1441// 梁笔记2// https://zouzhongliang.com3 456using namespace std;7 8 9enum EType{E_bool, E_char, E_short, E_int, E_long,10 E_float, E_double};11 12 13struct AnyData14{15 EType Eflag;16 union {17 bool bVal;18 char cVal;19 short sval;20 int ival;21 long lval;22 float fval;23 double dval;24 };25 26 AnyData(bool val){27 Eflag = E_bool;28 bVal = val;29 }30 AnyData(char val){31 Eflag = E_char;32 cVal = val;33 }34 AnyData(short val){35 Eflag = E_short;36 sval = val;37 }38 AnyData(int val){39 Eflag = E_int;40 ival = val;41 }42 AnyData(long val){43 Eflag = E_long;44 lval = val;45 }46 AnyData(float val){47 Eflag = E_float;48 fval = val;49 }50 AnyData(double val){51 Eflag = E_double;52 dval = val;53 }54 55 EType getType(){56 return Eflag;57 }58 59 60 void getVal(bool& val){61 if (Eflag == E_bool)62 val = bVal;63 }64 void getVal(char& val){65 if (Eflag == E_char)66 val = cVal;67 }68 void getVal(short& val){69 if (Eflag == E_short)70 val = sval;71 }72 void getVal(int& val){73 if (Eflag == E_int)74 val = ival;75 }76 void getVal(long& val){77 if (Eflag == E_long)78 val = lval;79 }80 void getVal(float& val){81 if (Eflag == E_float)82 val = fval;83 }84 void getVal(double& val){85 if (Eflag == E_double)86 val = dval ;87 }88 89 friend ostream& operator<<(ostream& out, AnyData& data);90 91};92 93ostream& operator<<(ostream& out, AnyData& data){94 switch(data.Eflag){95 case E_bool:96 out<< data.bVal;97 break;98 99 case E_char:100 out<< data.cVal;101 break;102 103 case E_short:104 out<< data.sval;105 break;106 107 case E_int:108 out<< data.ival;109 break;110 111 case E_long:112 out<< data.lval;113 break;114 115 case E_float:116 out<< data.fval;117 break;118 119 case E_double:120 out<< data.dval;121 break;122 }123 return out;124}125 126 127int main() {128 129 vector<AnyData> vecAnyData;130 vecAnyData.push_back(AnyData(false));131 vecAnyData.push_back(AnyData('q'));132 vecAnyData.push_back(AnyData((short)12));133 vecAnyData.push_back(AnyData(12));134 vecAnyData.push_back(AnyData((float)2.1));135 vecAnyData.push_back(AnyData(1.0));136 137 138 for(unsigned int i=0;i<vecAnyData.size();i++){139 cout<<vecAnyData[i]<<endl;140 }141 142 return 0;143}输出结果:
xxxxxxxxxx6102q31241252.161通过以上两种实现是可以支持对任意的基本类型进行存放的,但不可避免的会有穷举判断类型的过程,对自定义类型,需要借助void*实现,在这里并没有介绍具体的过程,接触到具体情况再继续了解。
回到读取.csv文件的问题,通过以上两种方法能够支持对未知类型数据的存入,读出时再去类型枚举。对于首行列名即类型名进行解析是否有必要?建立一个类型序列,循环进行行的读取,但这样的问题是在存入时需要根据类型序列确定,存入到确定且相同类型的容器中,读出时也要根据类型序列确定类型。
→back
Contact me
Please contact me about any suggestion(s): aliceb0b@hotmail.com.