如何在C++中将.csv文件的元素存储到二维向量中?
How can I store elements of a .csv file into a 2-dimensional vector in C++?
我正在尝试将.csv文件的内容存储到2D向量中。
我能够将.csv文件存储到单个维度向量中,我在类中的"read"函数中使用它来计算行和列的大小。以下是我当前的代码,在该行出现错误:
getline(ss,column.at(i),',');
如果我使用括号索引,错误说矢量下标超出范围,并且在使用 ___.at(( 时超出内存位置的范围。
我的读取函数运行,我能够获得正确的行和列大小,并将文件的内容存储到向量中。
class database {
public:
int row = 0;
int col = 0;
vector <vector<string>> dataset;
void read(string filename) {
vector<string> data;
ifstream file{ filename };
string line;
while (file)
{
getline(file, line);
string newline;
newline = line + "n";
data.push_back(newline);
}
col = count(data.at(0).begin(), data.at(0).end(), ',') + 1;
row = data.size() - 1;
}
void write(string filename){
ifstream file{ filename };
string line;
while (getline(file, line))
{
stringstream ss(line);
vector<string> column;
for (int i = 0; i < col; i++)
{
getline(ss, column.at(i), ',');
}
cout << column.size();
dataset.push_back(column);
}
}
};
我正在尝试使用 cout <<column.size(( 来查看它是否与我的数据向量的大小匹配,该向量是包含.csv文件所有内容的单维向量。
我会使用"更现代"的C++方法。
仍然所有人都链接到如何以C++读取和解析CSV文件?,这些问题来自2009年,现在已经有10多年的历史了。大多数答案也很古老且非常复杂。所以,也许是时候改变了。
在现代C++中,您拥有遍历范围的算法。你经常会看到类似"someAlgoritm(container.begin((, container.end((, someLambda("的内容。这个想法是,我们迭代一些类似的元素。
在您的情况下,我们会迭代输入字符串中的令牌,并创建子字符串。这称为标记化。
正是出于这个目的,我们有std::sregex_token_iterator
.因为我们有为此目的而定义的东西,我们应该使用它。
这个东西是一个迭代器。用于迭代字符串,因此是 sregex。开始部分定义了我们将在什么输入范围内进行操作,然后有一个std::regex
,用于输入字符串中应该匹配/或不应该匹配的内容。匹配策略的类型由最后一个参数给出。
- 1 -->给我我在正则表达式中定义的东西,然后 -
- 1 -->给我根据正则表达式不匹配的内容。
因此,现在我们了解了迭代器,我们可以 std::将令牌从迭代器复制到我们的目标,即std::string
std::vector
。由于我们不知道,我们怎么会有列,我们将使用std::back_inserter
作为目标。这将添加我们从std::sregex_token_iterator
获得的所有令牌,并将其附加到我们的std::vector<std::string>>
.我们拥有多少列并不重要。
好。这样的声明可能看起来像
std::copy( // We want to copy something
std::sregex_token_iterator // The iterator begin, the sregex_token_iterator. Give back first token
(
line.begin(), // Evaluate the input string from the beginning
line.end(), // to the end
re, // Add match a comma
-1 // But give me back not the comma but everything else
),
std::sregex_token_iterator(), // iterator end for sregex_token_iterator, last token + 1
std::back_inserter(cp.columns) // Append everything to the target container
);
现在我们可以理解这个复制操作是如何工作的了。
下一步。我们想从文件中读取。该文件还包含某种相同的数据。相同的数据是行。
如上所述,我们可以迭代类似的数据。如果是文件输入或其他什么。为此,C++有std::istream_iterator
.这是一个模板,作为模板参数,它获取应读取的数据类型,作为构造函数参数,它获取对输入流的引用。没关系,如果输入流是std::cin
,还是std::ifstream
或std::istringstream
。对于所有类型的流,行为都是相同的。
由于我们没有 SO 文件,我使用(在下面的示例中(std::istringstream
来存储输入 csv 文件。但是,当然,您可以通过定义一个文件来打开文件std::ifstream testCsv(filename)
。没关系。
有了std::istream_iterator
,我们迭代输入并读取类似的数据。在我们的例子中,一个问题是我们希望迭代特殊数据,而不是一些内置数据类型。
为了解决这个问题,我们定义了一个 Proxy 类,它为我们完成内部工作(我们不想知道如何,应该封装在代理中(。在代理中,我们覆盖类型转换运算符,以获得我们预期的std::istream_iterator
类型的结果。
也是最后一个重要步骤。std::vector
具有范围构造函数。它还具有许多其他构造函数,我们可以将其用于定义std::vector
类型的变量 .但就我们的目的而言,此构造函数最适合。
因此,我们定义一个变量 csv 并使用其范围构造函数,并为其提供范围的开始和范围的结束。并且,在我们的具体示例中,我们使用std::istream_iterator
的开始和结束迭代器。
如果我们结合以上所有内容,阅读完整的 CSV 文件是单行的,它是变量的定义,并调用其构造函数。
请参阅生成的代码:
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
#include <algorithm>
std::istringstream testCsv{ R"(0, 6/19/2019, 16:41:33, 33.972622, -117.323482, 24.25, 23.5, 23.25, 24.75, 25.5, 24.25, 25.25, 25.5, 24.5, 24, 24, 24.25, 25.5, 25.75, 25.25, 25, 24.5, 24.75, 24.75, 24.75, 25.25, 24.5, 24.5, 25.5, 23.75, 24.25, 24.75, 24, 24.25, 24, 24.5, 25, 24.25, 24, 24.25, 24.25, 24, 24.25, 24.5, 25.5, 24, 25, 24.5, 24.75, 24.5, 24.75, 24.75, 25.5, 24.5, 24.25, 24.25, 25.25, 25.25, 23.5, 25, 24.75, 24.5, 24.75, 25.5, 24.25, 23.5, 24, 25.25, 25, 605, 597, 515, 514, 509, 511, 508
0, 6/19/ 2019, 16:41:42, 33.972648, -117.323492, 24, 23.5, 23.75, 24.25, 25.5, 25.5, 25.25, 25.25, 25, 24.5, 24.25, 24.5, 25, 25.5, 25.5, 25.75, 24.25, 23.5, 24.75, 24.5, 24.25, 24.25, 24.5, 25.5, 24, 23.75, 24.5, 24, 24.25, 24, 24.75, 25.25, 25, 23.75, 24.75, 25.5, 25.5, 26, 24.75, 25.25, 24.5, 25, 25.25, 25.25, 26, 24.75, 24.5, 25.5, 24.5, 24.5, 25, 24.75, 24.25, 24.25, 25, 25, 24, 24, 24.75, 25, 23.25, 24.25, 25.5, 25.5, 609, 595, 1229, 1227, 1200, 1196, 1171
0, 6/19/2019, 16:41:49, 33.972643, -117.323479, 24.5, 23, 22.75, 24, 25.25, 25.5, 25, 26, 24.75, 24, 24, 24.75, 24.75, 25.25, 25.5, 26, 24.75, 24, 24.75, 25, 24.25, 24.25, 24.75, 26, 24.5, 23.5, 24.5, 24, 24, 24, 25, 25.75, 24.75, 23.25, 24.5, 24.5, 24.5, 25, 25.25, 25.25, 24, 25, 24.5, 25.25, 25.25, 25.25, 25.25, 25.5, 24.5, 24, 25.25, 25, 25, 24.25, 25, 25.25, 24.25, 24, 24.75, 25.25, 23.75, 24.25, 25, 25.5, 621, 601, 706, 725, 703, 707, 704
1, 6/19/2019, 16:41:55, 33.972631, -117.323483, 24.25, 23.75, 23.25, 24, 25.25, 25.25, 25.5, 26, 24.5, 24.25, 23.75, 24.5, 24.75, 25.5, 26, 25.5, 25, 23.75, 24.75, 24.75, 25.25, 25.25, 25, 26.25, 24.5, 23.5, 24.25, 25, 24.25, 24.25, 24.75, 25.75, 24.75, 23.75, 24.25, 24.25, 24.25, 24.5, 25.25, 25.25, 24.5, 24.5, 24.75, 25, 25.25, 26, 25.5, 25.25, 24.5, 24, 24.75, 25, 25, 25.25, 25.5, 25.5, 24.25, 25, 25, 25.75, 24.25, 24.5, 25.25, 25.5, 613, 602, 721, 720, 699, 704, 696
)" };
// Define Alias for Easier Reading
using Columns = std::vector<std::string>;
using CSV = std::vector<Columns>;
// Proxy for the input Iterator
struct ColumnProxy {
// Overload extractor. Read a complete line
friend std::istream& operator>>(std::istream& is, ColumnProxy& cp) {
// Read a line
std::string line; cp.columns.clear();
std::getline(is, line);
// The delimiter
const std::regex re(",");
// Split values and copy into resulting vector
std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1),
std::sregex_token_iterator(),
std::back_inserter(cp.columns));
return is;
}
// Type cast operator overload. Cast the type 'Columns' to std::vector<std::string>
operator std::vector<std::string>() const { return columns; }
protected:
// Temporary to hold the read vector
Columns columns{};
};
int main()
{
// Define variable CSV with its range constructor. Read complete CSV in this statement
CSV csv{ std::istream_iterator<ColumnProxy>(testCsv), std::istream_iterator<ColumnProxy>() };
// Print result. Go through all lines and then copy line elements to std::cout
std::for_each(csv.begin(), csv.end(), [](Columns& c) {
std::copy(c.begin(), c.end(), std::ostream_iterator<std::string>(std::cout, " ")); std::cout << "n"; });
}
我希望解释足够详细,可以让你有一个想法,你可以用现代C++做什么
- 我在二维向量中是否正确分配了内存
- 在二维向量或数组中可以存储的最大元素数是多少?
- 如何将整数字符串转换为整数的二维向量?
- 如何使用擦除和迭代器来删除二维向量中的项目
- 如何在二维向量c++中迭代col
- 如何在C++中将.csv文件的元素存储到二维向量中?
- std::vector::clear()ing 二维向量线程的内部向量安全吗?
- 在我为我的二维向量提供第一个值后控制台关闭
- 以C++输出二维向量
- 在 c++ 中赋值给二维向量
- 在二维向量的每一列中查找最大值
- 类对象初始化的二维向量
- 有没有办法像python:np.concatenate([x,y,z],axis=1)中那样沿着列连接三个二维向量C++
- 我如何在二维向量中存储一些数组
- 我可以push_back矩形形状到继承矩形形状的类的二维向量中吗?
- 二维向量的迭代器
- 在 STL 中,如何交换二维向量
- 创建一个二维向量阵列,遍历
- 创建二维向量的错误
- C++从文件中读取信息并从该信息构建二维向量