[英]How to read CSV data to pointers of struct vector in C++?
我想在 cpp 中将 csv 数据读取到结构向量,这是我写的,我想将虹膜数据集存储在结构向量 csv std::vector<Csv> *csv = new std::vector<Csv>;
的指针中std::vector<Csv> *csv = new std::vector<Csv>;
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
struct Csv{
float a;
float b;
float c;
float d;
std::string e;
};
int main(){
std::string colname;
// Iris csv dataset downloaded from
// https://gist.github.com/curran/a08a1080b88344b0c8a7
std::ifstream *myFile = new std::ifstream("iris.csv");
std::vector<Csv> *csv = new std::vector<Csv>;
std::string line;
// Read the column names
if(myFile->good())
{
// Extract the first line in the file
std::getline(*myFile, line);
// Create a stringstream from line
std::stringstream ss(line);
// Extract each column name
while(std::getline(ss, colname, ',')){
std::cout<<colname<<std::endl;
}
}
// Read data, line by line
while(std::getline(*myFile, line))
{
// Create a stringstream of the current line
std::stringstream ss(line);
}
return 0;
}
我不知道如何实现这部分代码,它输出带有浮点数和字符串的行。
// Read data, line by line
while(std::getline(*myFile, line))
{
// Create a stringstream of the current line
std::stringstream ss(line);
}
进化
我们从您的程序开始,并以您当前的程序风格完成它。 然后我们分析您的代码并将其重构为更具 C++ 风格的解决方案。 最后,我们展示了一个使用更多 OO 方法的现代 C++ 解决方案。
首先你完成的代码:
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
};
int main() {
std::string colname;
// Iris csv dataset downloaded from
// https://gist.github.com/curran/a08a1080b88344b0c8a7
std::ifstream* myFile = new std::ifstream("r:\\iris.csv");
std::vector<Csv>* csv = new std::vector<Csv>;
std::string line;
// Read the column names
if (myFile->good())
{
// Extract the first line in the file
std::getline(*myFile, line);
// Create a stringstream from line
std::stringstream ss(line);
// Extract each column name
while (std::getline(ss, colname, ',')) {
std::cout << colname << std::endl;
}
}
// Read data, line by line
while (std::getline(*myFile, line))
{
// Create a stringstream of the current line
std::stringstream ss(line);
// Extract each column
std::string column;
std::vector<std::string> columns{};
while (std::getline(ss, column, ',')) {
columns.push_back(column);
}
// Convert
Csv csvTemp{};
csvTemp.a = std::stod(columns[0]);
csvTemp.b = std::stod(columns[1]);
csvTemp.c = std::stod(columns[2]);
csvTemp.d = std::stod(columns[3]);
csvTemp.e = columns[4];
// STore new row data
csv->push_back(csvTemp);
}
// Show everything
for (const Csv& row : *csv)
std::cout << row.a << '\t' << row.b << '\t' << row.c << '\t' << row.d << '\t' << row.e << '\n';
return 0;
}
关于从 Csv 文件中读取列的问题,可以这样回答:
你需要一个临时向量。 然后使用std::getline
function 来拆分std::istringstream
中的数据并将生成的子字符串复制到向量中。 之后,我们使用字符串转换函数并将结果分配到临时 Csv 结构变量中。 完成所有转换后,我们将临时数据移动到生成的 csv 向量中,该向量包含所有行数据。
方案分析。
首先,也是最重要的,在 C++ 中,我们不对拥有的 memory 使用原始指针。 在大多数情况下,我们甚至不应该使用new
。 如果有的话,应该使用std::unique_ptr
和std::make_unique
。
但是我们根本不需要堆上的动态 memory 分配。 您可以简单地在函数堆栈上定义std::vector
。 与您的std::string colname;
行相同您还可以将std::vector
和std::ifstream
定义为普通的局部变量。 例如std::vector<Csv> csv{};
. 只是,如果您将此变量传递给另一个 function,则使用指针,但使用智能指针。
接下来,如果你打开一个文件,比如在std::ifstream myFile("r:\\iris.csv");
您不需要使用if (myFile->good())
测试文件流条件。 std::fstream
的 bool 运算符被覆盖,为您提供准确的信息。 请看这里。
现在,接下来也是最重要的。
您的源文件的结构是众所周知的。 有一个 header 有 5 个元素,然后是 4 个双精度数,然后结束一个没有空格的字符串。 这让生活变得非常轻松。
如果我们需要验证输入或者字符串中是否有空格,那么我们需要实现其他方法。 但是有了这个结构,我们就可以使用内置的 iostream 设施。 片段
// Read all data
Csv tmp{};
char comma;
while (myFile >> tmp.a >> comma >> tmp.b >> comma >> tmp.c >> comma >> tmp.d >> comma >> tmp.e)
csv.push_back(std::move(tmp));
会成功的。 很简单。
因此,重构后的解决方案可能如下所示:
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
};
int main() {
std::vector<Csv> csv{};
std::ifstream myFile("r:\\iris.csv");
if (myFile) {
if (std::string header{}; std::getline(myFile, header)) std::cout << header << '\n';
// Read all data
Csv tmp{};
char comma;
while (myFile >> tmp.a >> comma >> tmp.b >> comma >> tmp.c >> comma >> tmp.d >> comma >> tmp.e)
csv.push_back(std::move(tmp));
// Show everything
for (const Csv& row : csv)
std::cout << row.a << '\t' << row.b << '\t' << row.c << '\t' << row.d << '\t' << row.e << '\n';
}
return 0;
}
这已经更加紧凑了。 但还有更多。 . .
在下一步中,我们要添加更多面向 Object 的方法。
关键是对这些数据进行操作的数据和方法应该封装在一个 Object / class / 结构中。 只有 Csv 结构应该知道如何读取和写入其数据。
因此,我们覆盖了 Csv 结构的提取器和插入器运算符。 我们使用与以前相同的方法。 我们只是将读写封装在struct Csv中。
之后,主要的function会更加紧凑,使用更加合乎逻辑。
现在我们有:
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
friend std::istream& operator >> (std::istream& is, Csv& c) {
char comma;
return is >> c.a >> comma >> c.b >> comma >> c.c >> comma >> c.d >> comma >> c.e;
}
friend std::ostream& operator << (std::ostream& os, const Csv& c) {
return os << c.a << '\t' << c.b << '\t' << c.c << '\t' << c.d << '\t' << c.e << '\n';
}
};
int main() {
std::vector<Csv> csv{};
if (std::ifstream myFileStream("r:\\iris.csv"); myFileStream) {
if (std::string header{}; std::getline(myFileStream, header)) std::cout << header << '\n';
// Read all data
Csv tmp{};
while (myFileStream >> tmp)
csv.push_back(std::move(tmp));
// Show everything
for (const Csv& row : csv)
std::cout << row;
}
return 0;
}
好的。 已经相当不错了。 比特还有更多可能。
我们可以看到源数据有一个 header 和 Csv 数据。
这也可以建模成一个结构。 我们称之为鸢尾花。 我们还添加了一个提取器和插入器覆盖来封装所有 IO 操作。
此外,我们现在使用现代算法、正则表达式和 IO 迭代器。 我不确定,如果现在这太复杂了。 如果你有兴趣,那么我可以给你更多的信息。 但是现在,我将只向您展示代码。
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <regex>
#include <iterator>
const std::regex re{ "," };
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
// Overwrite extratcor for simple reading of data
friend std::istream& operator >> (std::istream& is, Csv& c) {
char comma;
return is >> c.a >> comma >> c.b >> comma >> c.c >> comma >> c.d >> comma >> c.e;
}
// Ultra simple inserter
friend std::ostream& operator << (std::ostream& os, const Csv& c) {
return os << c.a << "\t\t" << c.b << "\t\t" << c.c << "\t\t" << c.d << "\t\t" << c.e << '\n';
}
};
struct Iris {
// Iris data consits of header and then Csv Data
std::vector<std::string> header{};
std::vector<Csv> csv{};
// Overwrite extractor for generic reading from streams
friend std::istream& operator >> (std::istream& is, Iris& i) {
// First read header values;
if (std::string line{}; std::getline(is, line))
std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {}, std::back_inserter(i.header));
// Read all csv data
std::copy(std::istream_iterator<Csv>(is), {}, std::back_inserter(i.csv));
return is;
}
// Simple output. Copy data to stream os
friend std::ostream& operator << (std::ostream& os, const Iris& i) {
std::copy(i.header.begin(), i.header.end(), std::ostream_iterator<std::string>(os, "\t")); std::cout << '\n';
std::copy(i.csv.begin(), i.csv.end(), std::ostream_iterator<Csv>(os));
return os;
}
};
// Driver Code
int main() {
if (std::ifstream myFileStream("r:\\iris.csv"); myFileStream) {
Iris iris{};
// Read all data
myFileStream >> iris;
// SHow result
std::cout << iris;
}
return 0;
}
看看主要的 function 和它是多么容易。
如果您有任何疑问,请询问。
语言:C++17
使用 MS Visual Studio 2019 社区版编译和测试
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.