如何在 C++ 中将 CSV 数据读取到结构向量的指针?
How to read CSV data to pointers of struct vector in C++?
我想将 csv 数据读取到 cpp 中的结构向量,这就是我写的,我想将 iris 数据集存储在结构向量 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
函数拆分 std::istringstream
中的数据并将生成的子字符串复制到向量中。之后,我们使用字符串转换函数并将结果分配到临时 Csv 结构变量中。完成所有转换后,我们将临时文件移动到包含所有行数据的结果 csv 向量中。
程序分析。
首先,也是最重要的一点,在 C++ 中,我们不对自有内存使用原始指针。大多数情况下我们不应该使用 new
。如果有的话,应该使用 std::unique_ptr
和 std::make_unique
。
但是我们根本不需要在堆上动态分配内存。您可以简单地在函数堆栈上定义 std::vector
。与您的 std::string colname;
行一样,您也可以将 std::vector
和 std::ifstream
定义为普通局部变量。例如 std::vector<Csv> csv{};
。只是,如果你将这个变量传递给另一个函数,那么使用指针,但是智能指针。
接下来,如果您打开一个文件,就像在 std::ifstream myFile("r:\iris.csv");
中一样,您不需要使用 if (myFile->good())
测试文件流条件。 std::fstream
s 布尔运算符被覆盖,为您提供准确的信息。请参阅 here。
现在,接下来也是最重要的。
你的源文件的结构是众所周知的。有一个包含 5 个元素的 header,然后是 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中。
之后main函数会更加紧凑,使用起来也更加符合逻辑
现在我们有:
#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-operations.
此外,我们现在还使用现代算法、正则表达式和 IO-iterators。我不确定,如果这现在太复杂了。如果您有兴趣,那么我可以为您提供更多信息。但现在,我只向您展示代码。
#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;
}
看看主要功能,它是多么简单。
如有疑问,请提问。
语言:C++17
使用 MS Visual Studio 2019 社区版编译和测试
我想将 csv 数据读取到 cpp 中的结构向量,这就是我写的,我想将 iris 数据集存储在结构向量 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
函数拆分 std::istringstream
中的数据并将生成的子字符串复制到向量中。之后,我们使用字符串转换函数并将结果分配到临时 Csv 结构变量中。完成所有转换后,我们将临时文件移动到包含所有行数据的结果 csv 向量中。
程序分析。
首先,也是最重要的一点,在 C++ 中,我们不对自有内存使用原始指针。大多数情况下我们不应该使用 new
。如果有的话,应该使用 std::unique_ptr
和 std::make_unique
。
但是我们根本不需要在堆上动态分配内存。您可以简单地在函数堆栈上定义 std::vector
。与您的 std::string colname;
行一样,您也可以将 std::vector
和 std::ifstream
定义为普通局部变量。例如 std::vector<Csv> csv{};
。只是,如果你将这个变量传递给另一个函数,那么使用指针,但是智能指针。
接下来,如果您打开一个文件,就像在 std::ifstream myFile("r:\iris.csv");
中一样,您不需要使用 if (myFile->good())
测试文件流条件。 std::fstream
s 布尔运算符被覆盖,为您提供准确的信息。请参阅 here。
现在,接下来也是最重要的。
你的源文件的结构是众所周知的。有一个包含 5 个元素的 header,然后是 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中。
之后main函数会更加紧凑,使用起来也更加符合逻辑
现在我们有:
#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-operations.
此外,我们现在还使用现代算法、正则表达式和 IO-iterators。我不确定,如果这现在太复杂了。如果您有兴趣,那么我可以为您提供更多信息。但现在,我只向您展示代码。
#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;
}
看看主要功能,它是多么简单。
如有疑问,请提问。
语言:C++17
使用 MS Visual Studio 2019 社区版编译和测试