C++ 如果没有具体的数据结构,您将如何在某种数据结构中存储CSV数据:C+中的列数、列类型等+;
好的,基本上我需要从CSV文件中读取数据,并将其存储在某种数据结构中。CSV数据看起来像这样:C++ 如果没有具体的数据结构,您将如何在某种数据结构中存储CSV数据:C+中的列数、列类型等+;,c++,csv,C++,Csv,好的,基本上我需要从CSV文件中读取数据,并将其存储在某种数据结构中。CSV数据看起来像这样: year,position,MVP,entity INT,STRING,BOOL,STRING 2020,FORWARD,TRUE,Lionel Messi 2020,MIDFIELDER,FALSE,Jordan Henderson 2020,GOALKEEPER,FALSE,David De Gea 2020,DEFENDER,FALSE,Virgil van Dijk startYear,jo
year,position,MVP,entity
INT,STRING,BOOL,STRING
2020,FORWARD,TRUE,Lionel Messi
2020,MIDFIELDER,FALSE,Jordan Henderson
2020,GOALKEEPER,FALSE,David De Gea
2020,DEFENDER,FALSE,Virgil van Dijk
startYear,job,entity
INT,STRING,STRING
2001,SALES ASSOCIATE,Jackie Cruz
1992,GENERAL MANAGER,Jorge Almandra
2004,CUSTODIAN,Jeffrey Howie
2018,ELECTRICIAN,Katie Moody
前两行将告诉您属性的名称及其类型
我知道如何从CSV文件中读取数据,但问题是,当列数、属性类型(bool、int等)可能不同时,我真的不知道存储所述数据的最佳数据结构是什么
起初,我认为一个由行对象向量表示的表是可行的,但只有当我确切地知道有多少属性、它们的类型是什么、它们的名称是什么等时,这才有效
我想我可以基于数据的元数据来存储它,比如属性、属性类型、行位置等等,但我还不知道如何扩展这个想法
任何帮助都将不胜感激
编辑:
因此,基本上我的程序必须使用类似于我上面发布的结构的CSV文件,但是每个CSV文件可能有不同的列数、不同的属性类型,等等
一个csv文件可以像上面的示例一样,另一个可以像这样:
year,position,MVP,entity
INT,STRING,BOOL,STRING
2020,FORWARD,TRUE,Lionel Messi
2020,MIDFIELDER,FALSE,Jordan Henderson
2020,GOALKEEPER,FALSE,David De Gea
2020,DEFENDER,FALSE,Virgil van Dijk
startYear,job,entity
INT,STRING,STRING
2001,SALES ASSOCIATE,Jackie Cruz
1992,GENERAL MANAGER,Jorge Almandra
2004,CUSTODIAN,Jeffrey Howie
2018,ELECTRICIAN,Katie Moody
我仍然需要能够将数据存储到某种数据结构中,即使列的数量和类型不同。这里是一种可能的解决方案 我讨厌它。我决不会做那样的事。因为设计理念或要求已经毫无意义 或者,我们使用类型并知道哪个列具有什么类型,或者我们只是对所需的上下文使用适合所有类型。在本例中,只需一个
std::string
但是动态地这样做会导致非常难看且不可维护的代码
这里的解决方案是std::any。但也许班级等级制会更好。我稍后再试
请查看以下代码:
#include <iostream>
#include <sstream>
#include <vector>
#include <regex>
#include <string>
#include <iterator>
#include <algorithm>
#include <utility>
#include <any>
#include <map>
#include <tuple>
// the delimiter for the csv
const std::regex re(",");
// One DataRow from the csv file
struct DataRow {
std::vector<std::string> columns{};
friend std::istream& operator >> (std::istream& is, DataRow& dr) {
// Read one complete line
if (std::string line{}; std::getline(is, line)) {
// Split the string, containing the complete line into parts
dr.columns.clear();
std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {}, std::back_inserter(dr.columns));
}
return is;
}
};
struct CSV {
protected:
// Conversion functions
std::any stringToAnySTRING(const std::string& s) { return s; }
std::any stringToAnyBOOL(const std::string& s) { bool result{ false }; if (s == "TRUE") result = true; return result; }
std::any stringToAnyINT(const std::string& s) { int result = std::stoi(s); return result; }
std::any stringToAnyLONG(const std::string& s) { long result = std::stol(s); return result; }
// Making Reading easier
using ConvertToAny = std::any(CSV::*)(const std::string&);
// Map conversion functions to type strings
std::map<std::string, ConvertToAny> converter{
{"STRING", &CSV::stringToAnySTRING},
{"BOOL", &CSV::stringToAnyBOOL},
{"INT", &CSV::stringToAnyINT},
{"LONG", &CSV::stringToAnyLONG}
};
public:
// Header, Types and data as std::any
std::vector<std::string> header{};
std::vector<std::string> types{};
std::vector<std::vector<std::any>> data{};
// Extractor operator
friend std::istream& operator >> (std::istream& is, CSV& c) {
// Read header line
if (std::string line{}; std::getline(is, line)) {
// Split header line into sub strings
c.header.clear();
std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {}, std::back_inserter(c.header));
// Read types line
if (std::getline(is, line)) {
// Spit types into sub strings
c.types.clear();
std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {}, std::back_inserter(c.types));
// Read all data, so all lines, split them and convert them to the desired data type
c.data.clear();
// This will read all lines and split them into columns
std::vector<DataRow> drs(std::istream_iterator<DataRow>(is), {});
// Make at least one plausibility check, that all rows have the same number of columns
size_t minDataLength = std::min_element(drs.begin(), drs.end(), [](const DataRow& dr1, const DataRow& dr2)
{return dr1.columns.size() < dr2.columns.size(); })->columns.size();
if (c.header.size() == c.types.size() && c.types.size() == minDataLength) {
// Now convert all columns into the type denoted by the read type array and store them as any data
// Double transform because of 2 dimensional array
std::transform(drs.begin(), drs.end(), std::back_inserter(c.data), [&c](const DataRow& dr) {
std::vector<std::any> va{};
// This is the conversion into a type defined by the types array
// Anybody who understands this transfrom will get the Nobel price for Obfuscation
std::transform(dr.columns.begin(), dr.columns.end(), std::back_inserter(va),
[&c, i = 0U](const std::string& s) mutable {return (c.*(c.converter[c.types[i++]]))(s); });
return va; });
}
}
}
return is;
}
// Inserter operator
friend std::ostream& operator << (std::ostream& os, const CSV& c) {
// Write header
os << "Header: ";
std::copy(c.header.begin(), c.header.end(), std::ostream_iterator<std::string>(os, " "));
// And the type names
os << "\nTypes: ";
std::copy(c.types.begin(), c.types.end(), std::ostream_iterator<std::string>(os, " "));
os << "\n\nData:\n";
// And the types. Arrgh. How ugly
std::for_each(c.data.begin(), c.data.end(), [&c,&os](const std::vector<std::any>& va) {
for (size_t i = 0U; i < va.size(); ++i) {
if (c.types[i] == "INT") { int v = std::any_cast<int>(va[i]); os << v << " "; }
else if (c.types[i] == "LONG") { long v = std::any_cast<long>(va[i]); os << v << " "; }
else if (c.types[i] == "STRING") { std::string v = std::any_cast<std::string>(va[i]); os << v << " "; }
else if (c.types[i] == "BOOL") { bool v = std::any_cast<bool>(va[i]); os << v << " "; }
}
os << "\n";
});
return os;
}
};
// The data. Does not matter if file or stringstream. Is the same
std::istringstream csvFile{ R"(year,category,winner,entity
INT,STRING,BOOL,STRING
2015,CHEF OF THE YEAR,FALSE,John Doe
2015,CHEF OF THE YEAR,FALSE,Bob Brown
2015,CHEF OF THE YEAR,TRUE,William Thorton
2015,CHEF OF THE YEAR,FALSE,Jacob Smith)" };
int main() {
// Define varaiable of type csv
CSV csv{};
// Read from somewhere
csvFile >> csv;
// Show some debug output
std::cout << csv;
return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
//csv的分隔符
常量std::正则表达式re(“,”);
//csv文件中的一个数据行
结构数据行{
std::向量列{};
friend std::istream&operator>>(std::istream&is、DataRow&dr){
//读一整行
if(std::string line{};std::getline(is,line)){
//将包含完整行的字符串拆分为多个部分
dr.columns.clear();
std::copy(std::sregex_令牌_迭代器(line.begin(),line.end(),re,-1),{},std::back_插入器(dr.columns));
}
回报是;
}
};
结构CSV{
受保护的:
//转换函数
std::anystringtoanystring(const std::string&s){return s;}
std::any stringToAnyBOOL(const std::string&s){bool result{false};if(s==“TRUE”)result=TRUE;return result;}
std::any stringToAnyINT(const std::string&s){int result=std::stoi(s);返回结果;}
std::any stringToAnyLONG(const std::string&s){long result=std::stol(s);返回结果;}
//使阅读更容易
使用ConvertToAny=std::any(CSV::*)(const std::string&);
//将转换函数映射到类型字符串
映射转换器{
{“STRING”,&CSV::stringToAnySTRING},
{“BOOL”,&CSV::stringToAnyBOOL},
{“INT”,&CSV::stringToAnyINT},
{“LONG”,&CSV::stringToAnyLONG}
};
公众:
//标题、类型和数据为std::any
std::向量头{};
std::向量类型{};
std::矢量数据{};
//提取器操作员
friend std::istream&operator>>(std::istream&is、CSV&c){
//读取标题行
if(std::string line{};std::getline(is,line)){
//将标题行拆分为子字符串
c、 header.clear();
std::copy(std::sregex_令牌_迭代器(line.begin(),line.end(),re,-1),{},std::back_插入器(c.header));
//读取类型行
if(std::getline(is,line)){
//将类型吐入子字符串
c、 类型。清除();
std::copy(std::sregex_令牌_迭代器(line.begin(),line.end(),re,-1),{},std::back_插入器(c.types));
//读取所有数据,因此读取所有行,拆分它们并将它们转换为所需的数据类型
c、 data.clear();
//这将读取所有行并将它们拆分为列
std::vector drs(std::istream_迭代器(is),{});
//至少进行一次合理性检查,确保所有行的列数相同
size\t minDataLength=std::min\u元素(drs.begin()、drs.end()、[](常量数据行和dr1、常量数据行和dr2)
{返回dr1.columns.size()columns.size();
if(c.header.size()==c.types.size()&&c.types.size()==MindAtalLength){
//现在,将所有列转换为读取类型数组表示的类型,并将它们存储为任何数据
//二维数组的双重变换
std::transform(drs.begin()、drs.end()、std::back_插入器(c.data)、[&c](const-DataRow&dr){
std::向量va{};
//这是到由types数组定义的类型的转换
//任何理解这一转变的人都将因混淆而获得诺贝尔奖
std::transform(dr.columns.begin()、dr.columns.end()、std::back_插入器(va),
[&c,i=0U](const std::string&s)可变{return(c.*(c.converter[c.types[i++]])(s);});
返回va;});
}
}
}
回报是;
}
//插入器操作员
friend std::ostream&operator如果列不固定,您将如何对数据进行有意义的处理?我希望每个记录(行)都是一个s