C++中如何用同一变量名存储不同类型的CSV读取数据?
C++ CSV读取类的统一数据变量解决方案
问题描述
我正在编写一个C++程序读取CSV文件,已实现std::string * readdatastr()和double * readdatalf()两个函数,分别用于将文件中的字符串型、双精度浮点型数据存入矩阵,并封装在readcsv类中:
class readcsv{ public: int nrows; int ncols; std::string * datastr; double * datalf; void getdim(int * nrows, int * ncols){ //function to get column and row number of the file } std::string * readdatastr(FILE * filept, int nrows, int ncols){ //function to read data if data is std::string } double * readdatalf(FILE * filept, int nrows, int ncols){ //function to read data if data is double } readcsv(FILE * filept, bool isstr, int nrows, int ncols){ if(isstr){ datastr=new std::string[nrows*ncols]; datastr=readdatastr(); }else{ datalf=new double[nrows*ncols]; datalf=readdatalf(); } } };
当前实现可正常运行,但存储数据的变量名因数据类型不同分为datastr和datalf,使用繁琐且后续易引发问题。希望实现统一变量名,或得到单一类型对应的数据集。
解决方案
方法1:模板类(推荐,类型安全且无冗余)
通过模板类让类的行为随类型参数变化,用统一的变量名data存储不同类型数据,同时避免代码重复。
#include <string> #include <cstdio> #include <type_traits> template<typename T> class readcsv { public: int nrows; int ncols; T* data; void getdim(int* nrows, int* ncols) { // 实现获取CSV行列数的逻辑 } // 通用读取声明,通过特化实现不同类型逻辑 T* readdata(FILE* filept, int nrows, int ncols); readcsv(FILE* filept, int nrows, int ncols) : nrows(nrows), ncols(ncols) { data = readdata(filept, nrows, ncols); } ~readcsv() { delete[] data; } // 禁用拷贝构造与赋值,避免内存泄漏 readcsv(const readcsv&) = delete; readcsv& operator=(const readcsv&) = delete; }; // 特化std::string类型的读取逻辑 template<> std::string* readcsv<std::string>::readdata(FILE* filept, int nrows, int ncols) { std::string* result = new std::string[nrows * ncols]; // 这里实现CSV字符串数据的读取逻辑 return result; } // 特化double类型的读取逻辑 template<> double* readcsv<double>::readdata(FILE* filept, int nrows, int ncols) { double* result = new double[nrows * ncols]; // 这里实现CSV浮点数据的读取逻辑 return result; }
使用方式:
FILE* fp = fopen("data.csv", "r"); int rows = 0, cols = 0; // 先调用getdim获取行列数 // 读取字符串型数据 readcsv<std::string> str_reader(fp, rows, cols); std::string* str_data = str_reader.data; // 读取浮点型数据 readcsv<double> double_reader(fp, rows, cols); double* double_data = double_reader.data;
方法2:使用std::variant(单实例支持多类型)
如果需要在同一个类实例中兼容两种数据类型,可使用C++17引入的std::variant,用单一变量存储不同类型的指针。
#include <string> #include <cstdio> #include <variant> #include <functional> class readcsv { public: int nrows; int ncols; std::variant<std::string*, double*> data; void getdim(int* nrows, int* ncols) { // 实现获取行列数的逻辑 } std::string* readdatastr(FILE* filept, int nrows, int ncols) { std::string* result = new std::string[nrows * ncols]; // 字符串读取逻辑 return result; } double* readdatalf(FILE* filept, int nrows, int ncols) { double* result = new double[nrows * ncols]; // 浮点读取逻辑 return result; } readcsv(FILE* filept, bool isstr, int nrows, int ncols) : nrows(nrows), ncols(ncols) { data = isstr ? readdatastr(filept, nrows, ncols) : readdatalf(filept, nrows, ncols); } ~readcsv() { // 遍历variant释放对应类型的内存 std::visit([](auto* ptr) { delete[] ptr; }, data); } // 禁用拷贝构造与赋值 readcsv(const readcsv&) = delete; readcsv& operator=(const readcsv&) = delete; };
使用方式:
readcsv reader(fp, true, rows, cols); if (std::holds_alternative<std::string*>(reader.data)) { std::string* str_data = std::get<std::string*>(reader.data); // 使用字符串数据 } else { double* double_data = std::get<double*>(reader.data); // 使用浮点数据 }
方法3:基类+派生类(多态实现统一接口)
通过多态定义统一的基类接口,不同数据类型对应不同派生类,实现数据访问的统一。
#include <string> #include <cstdio> class BaseCSVReader { public: int nrows; int ncols; BaseCSVReader(int nrows, int ncols) : nrows(nrows), ncols(ncols) {} virtual ~BaseCSVReader() = default; // 统一数据访问接口 virtual std::string get_string(int idx) const = 0; virtual double get_double(int idx) const = 0; }; class StringCSVReader : public BaseCSVReader { public: std::string* data; StringCSVReader(FILE* filept, int nrows, int ncols) : BaseCSVReader(nrows, ncols) { data = new std::string[nrows * ncols]; // 字符串读取逻辑 } ~StringCSVReader() override { delete[] data; } std::string get_string(int idx) const override { return data[idx]; } double get_double(int idx) const override { return std::stod(data[idx]); // 字符串转浮点 } }; class DoubleCSVReader : public BaseCSVReader { public: double* data; DoubleCSVReader(FILE* filept, int nrows, int ncols) : BaseCSVReader(nrows, ncols) { data = new double[nrows * ncols]; // 浮点读取逻辑 } ~DoubleCSVReader() override { delete[] data; } std::string get_string(int idx) const override { return std::to_string(data[idx]); // 浮点转字符串 } double get_double(int idx) const override { return data[idx]; } };
使用方式:
BaseCSVReader* reader; if (is_str_type) { reader = new StringCSVReader(fp, rows, cols); } else { reader = new DoubleCSVReader(fp, rows, cols); } // 通过统一接口访问数据 std::string str_val = reader->get_string(0); double double_val = reader->get_double(0); delete reader;
内容的提问来源于stack exchange,提问作者Giacomo Marcon
相关产品推荐
相关产品推荐

