被问到在用C++处理.csv文件时,每行有多种类型的数据,如果在首行可以读取到不同列的类型,接下来如何读取剩下的数据?
问题在于C++不是动态类型的语言,如何在运行时确定存储类型,参考知乎上的一些回答,了解到直接可以使用C++17支持的std::any/std::variant
来进行对未知类型的存储。其它的一些方法基本会通过union来实现一个结构支持不同的类型。
我试验了使用any的情况,发现基于any的实现可以很容易的存入容器比如vector<std::any>
,但是当需要取出来时还是需要去对所有类型进行一个判断比较,使用std::any_cast
去得到对应的类型。
xxxxxxxxxx
341
2
3
4
5using namespace std;
6
7int main()
8{
9 vector<any> anys;
10 int a = 10;
11 double b = 2.5;
12 char c = 'f';
13 string d = "Hello";
14
15 anys.emplace_back(a);
16 anys.emplace_back(b);
17 anys.emplace_back(c);
18 anys.emplace_back(d);
19
20 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容器为例,实现存放不同类型完整代码:
xxxxxxxxxx
1441// 梁笔记
2// https://zouzhongliang.com
3
4
5
6using namespace std;
7
8
9enum EType{E_bool, E_char, E_short, E_int, E_long,
10 E_float, E_double};
11
12
13struct AnyData
14{
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}
输出结果:
xxxxxxxxxx
610
2q
312
412
52.1
61
通过以上两种实现是可以支持对任意的基本类型进行存放的,但不可避免的会有穷举判断类型的过程,对自定义类型,需要借助void*实现,在这里并没有介绍具体的过程,接触到具体情况再继续了解。
回到读取.csv文件的问题,通过以上两种方法能够支持对未知类型数据的存入,读出时再去类型枚举。对于首行列名即类型名进行解析是否有必要?建立一个类型序列,循环进行行的读取,但这样的问题是在存入时需要根据类型序列确定,存入到确定且相同类型的容器中,读出时也要根据类型序列确定类型。
→back
Contact me
Please contact me about any suggestion(s): aliceb0b@hotmail.com.