如何将字符串转换为可操作数组
How to convert string to an operable array?
我有一个CSV文件,其中包含以下数据,例如
X1 X2 X3
Y1 aa dd 15
Y2 bb ee 20
Y3 cc ff 25
如何为第二列计算总和,并在每个值附近写入总和的百分比(在CSV文件中添加一个新列(?例如:
X1 X2 X3 X4
Y1 aa dd 15 25%
Y2 bb ee 20 33%
Y3 cc ff 25 42%
我已经将第四列中的所有值放入一个向量中。
#include<iostream>
#include<string>
#include<fstream>
#include<sstream>
#include<vector>
using namespace std;
int main()
{
string line;
ifstream infile("aaa.csv");
vector<float> investment;
int i = 0;
while (getline(infile, line))
{
i++;
if (i == 1) {
continue;
}
istringstream sin(line);
vector<string> fields;
string field;
while (getline(sin, field, ','))
{
fields.push_back(field);
}
double p;
string temp = fields[3];
}
}
现在我很困惑如何将字符串转换为浮点型数组,以便进行一些计算。
似乎您想读取某种csv数据。我认为您显示的数据只是一个示例,而不是真正的文件。因为稍后,您将搜索逗号。
阅读此类CSV文件是一项标准任务,我将为您提供详细解释。最后,文件的所有读取都将在一行中完成。如果文件看起来不一样,那么这也可以。
然后你想做一些数学运算,然后流式输出结果。
为了实现这个任务,我将使用"现代"C++元素。
在现代C++中,您可以使用在范围上迭代的算法。您经常会看到类似"someAlgorim(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
);
现在我们可以了解这种复制操作是如何工作的。
请注意,在下面的例子中,数字将从字符串转换为浮点值。因此,我们不会使用std::copy
,而是使用std::transform
。但是机制当然是一样的。
下一步。我们想从文件中读取。该文件还包含某种相同的数据。相同的数据是行。
至于上面,我们可以迭代类似的数据。如果是文件输入或其他什么。为此,C++具有CCD_ 10。这是一个模板,作为模板参数,它获取应该读取的数据类型,作为构造函数参数,它获得对输入流的引用。不管输入流是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文件是一个一行,它是一个变量的定义,并调用其范围构造函数。
数学运算非常简单。我们将计算总和,并将得到的百分比作为一个新列相加。为此,我们将使用2个std::for_each
语句。
请查看结果代码:
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
#include <algorithm>
std::istringstream testCsv{ R"(00,01,15
10,11,20
20,21,25)" };
// Define Alias for easier Reading
using Columns = std::vector<float>;
using CSV = std::vector<Columns>;
// The delimiter
const std::regex re(",");
// 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);
if (!line.empty()) {
// Split values and copy into resulting vector
std::transform(std::sregex_token_iterator(line.begin(), line.end(), re, -1),
std::sregex_token_iterator(),
std::back_inserter(cp.columns),
[](const std::string& s){ return std::stof(s);});
}
return is;
}
// Type cast operator overload. Cast the type 'Columns' to std::vector<std::string>
operator Columns() 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, So, one liner
CSV csv{ std::istream_iterator<ColumnProxy>(testCsv), std::istream_iterator<ColumnProxy>() };
// Calculate the sum of the 2nd column
float sum{0.0};
std::for_each(csv.begin(),csv.end(),[&sum](const Columns& c){sum += c.at(2);});
// Calculate the percentage of the element from the overall sum
std::for_each(csv.begin(),csv.end(),[&sum](Columns& c){c.push_back(c.at(2)/sum);});
// Debug Output. Print matrix to std::cout
std::for_each(csv.begin(), csv.end(), [](const Columns &c) {
std::copy(c.begin(), c.end(), std::ostream_iterator<float>(std::cout, " ")); std::cout << 'n'; });
return 0;
}
其结果是一段具有高度灵活性的超简单代码。
在函数main中,您将只看到一些语句。
我希望它能帮助。
- 如何使用Crypto++并为RSA返回可打印的字节/字符数组
- 对字符数组中的元素执行逐位操作
- 给定一个整数数组,需要在Max_Heap上运行操作。得到错误"segmentation fault",有什么想法吗?(C++)
- 为什么数组大小信息可用于"sizeof"运算符和 delete[] 运算符,但在将数组作为参数传递到
- 可视化 如何在C++中将字符数组转换为 FILE 类型
- 创建异构顶点数据数组的可移植方法
- 如何在可执行文件中存储常量数组?
- 在矢量数组C++中使用算法库操作
- C++ 黑客等级 - 数组操作
- 从非类型模板参数声明 constexpr 数组的可移植方法
- C++ 中数组(单引号和双引号)的可视常量字符* 与字符
- 使用 std::type T 的数组来构造可从 T 构造的 std::数组
- 使用字符数组堆叠。如何执行"+"和"*"操作?
- C++数组指针上的删除操作
- 如何有效地使用 std::async 对指针数组执行操作
- 数组A中最大的元素数量可以通过使用操作最多可将其降低至1
- 可被数组中的任何数字整除的数字
- C++递归函数,可反转数组索引在两个边界之间的顺序
- 在DirectX 9中使用可扩展数组绘制三角形
- c++轻可追加数组容器