如何在 C++ 中将字符串拆分为数组
Posted
技术标签:
【中文标题】如何在 C++ 中将字符串拆分为数组【英文标题】:How can I split a string into an array in C++ 【发布时间】:2017-09-17 19:57:36 【问题描述】:我需要捕获一个方程(例如 2x^2-1x)我有这段代码可以捕获它
string eq;
cin>>eq;
为了解决它,我需要将前一个字符串的每个字符拆分为一个数组,然后通过循环继续解决它。我想我知道如何做循环部分,但我怎样才能把它分成一个数组?还是有更简单的方法来做到这一点?
【问题讨论】:
你需要一个分词器。恐怕你必须实现它。 你需要一个分词器。恐怕你必须实现它。 你的方程总是ax^2+bx+c
的形式,你需要解析a
、b
和c
的值?
在 C++ 中没有“更简单的方法”可以做任何事情。 C++ 可以说是当代最复杂的通用编程语言。您将需要学习如何检查std::string
的内容,如何提取字符串的各个部分,以及如何将片段放入数组或向量中。有关详细信息,请参阅您的 C++ 书籍。
@StephanLechner 你正在做某事。这可能是众多多项式练习之一。 (具有讽刺意味的是,早在我添加我的方式过于笼统的答案之前,我就已经投票结束了“过于宽泛”)
【参考方案1】:
您实际上并没有尝试将字符串拆分为数组。数组不会给你带来任何东西。你想要一个表达式树。或者至少是动态评估的递归下降解析。这会更容易,但效率较低。
在 *** 上一定有很多关于递归下降表达式解析器的问题/答案。使用搜索框获取想法。
演示
为了完全矫枉过正,这里有一个带有动态(单字母)变量和一些测试用例的公式评估函数示例。
它使用 C++14 和 Boost Spirit X3。这里的“递归下降”解析器是从PEG rules 生成的,而不是手写的。
Live On Coliru
//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <map>
using VarMap = std::map<char, double>;
namespace formula
namespace detail
using namespace boost::spirit::x3;
#define BIN(f) ([](auto &ctx) _val(ctx) = f(_val(ctx), _attr(ctx)); )
#define BINOP(op) BIN(([](auto a, auto b) return a op b; ))
#define IDENT ([](auto &ctx) _val(ctx) = _attr(ctx); )
static VarMap var_map;
auto lookup = [](auto& ctx) _val(ctx) = var_map.at(_attr(ctx)); ;
rule<struct f_, double> factor "factor";
rule<struct t_, double> term "term";
rule<struct x_, double> expo "expo";
auto var = rule<struct _v, double> "var"
= alpha [lookup];
auto literal = !lit('-') >> double_;
auto simple = rule<struct _l, double> "simple"
= ('(' >> term >> ')') | var | literal;
auto expo_def
= simple [IDENT] >> *('^' >> expo)[BIN(pow)];
auto factor_def = expo [IDENT] >> *(
'*' >> factor [BINOP(*)]
| '/' >> factor [BINOP(/)]
| factor [BINOP(*)]
);
auto term_def = factor [IDENT] >> *(
'+' >> term [BINOP(+)]
| '-' >> term [BINOP(-)]
);
BOOST_SPIRIT_DEFINE(expo, factor, term)
auto expr = skip(space) [eps > term > eoi];
struct evaluation_error : std::runtime_error
evaluation_error(std::string const& msg) : std::runtime_error(msg)
;
double eval(std::string const& formula, VarMap vars)
using namespace std::string_literals;
detail::var_map = vars;
double value;
try
bool ok = parse(begin(formula), end(formula), detail::expr, value);
assert(ok);
return value;
catch(boost::spirit::x3::expectation_failure<std::string::const_iterator> const& e)
throw evaluation_error("syntax: expect " + e.which() + " at '" + std::string(e.where(), formula.end()) + "'");
catch(std::out_of_range const& e)
throw evaluation_error("variable undefined");
catch(std::exception const& e)
throw evaluation_error("eval: "s + e.what());
int main()
for (auto formula : "", "0", "2", "x", "2x",
"x^2",
"2x^2",
"2x^2-1x",
"2x^2-sin x",
"x^(1/2)",
"(x^(1/2))^2",
)
try
std::cout << "Function f(x) -> " << formula << "\n";
for (double x = 0; x < 10; x += 1)
std::cout << " - f(" << x << ") -> " << formula::eval(formula, 'x', x) << "\n";
catch(formula::evaluation_error const& e)
std::cout << "Oops: " << e.what() << "\n";
打印
Function f(x) ->
- f(0) -> Oops: syntax: expect term at ''
Function f(x) -> 0
- f(0) -> 0
- f(1) -> 0
- f(2) -> 0
- f(3) -> 0
- f(4) -> 0
- f(5) -> 0
- f(6) -> 0
- f(7) -> 0
- f(8) -> 0
- f(9) -> 0
Function f(x) -> 2
- f(0) -> 2
- f(1) -> 2
- f(2) -> 2
- f(3) -> 2
- f(4) -> 2
- f(5) -> 2
- f(6) -> 2
- f(7) -> 2
- f(8) -> 2
- f(9) -> 2
Function f(x) -> x
- f(0) -> 0
- f(1) -> 1
- f(2) -> 2
- f(3) -> 3
- f(4) -> 4
- f(5) -> 5
- f(6) -> 6
- f(7) -> 7
- f(8) -> 8
- f(9) -> 9
Function f(x) -> 2x
- f(0) -> 0
- f(1) -> 2
- f(2) -> 4
- f(3) -> 6
- f(4) -> 8
- f(5) -> 10
- f(6) -> 12
- f(7) -> 14
- f(8) -> 16
- f(9) -> 18
Function f(x) -> x^2
- f(0) -> 0
- f(1) -> 1
- f(2) -> 4
- f(3) -> 9
- f(4) -> 16
- f(5) -> 25
- f(6) -> 36
- f(7) -> 49
- f(8) -> 64
- f(9) -> 81
Function f(x) -> 2x^2
- f(0) -> 0
- f(1) -> 2
- f(2) -> 8
- f(3) -> 18
- f(4) -> 32
- f(5) -> 50
- f(6) -> 72
- f(7) -> 98
- f(8) -> 128
- f(9) -> 162
Function f(x) -> 2x^2-1x
- f(0) -> 0
- f(1) -> 1
- f(2) -> 6
- f(3) -> 15
- f(4) -> 28
- f(5) -> 45
- f(6) -> 66
- f(7) -> 91
- f(8) -> 120
- f(9) -> 153
Function f(x) -> 2x^2-sin x
- f(0) -> Oops: variable undefined
Function f(x) -> x^(1/2)
- f(0) -> 0
- f(1) -> 1
- f(2) -> 1.41421
- f(3) -> 1.73205
- f(4) -> 2
- f(5) -> 2.23607
- f(6) -> 2.44949
- f(7) -> 2.64575
- f(8) -> 2.82843
- f(9) -> 3
Function f(x) -> (x^(1/2))^2
- f(0) -> 0
- f(1) -> 1
- f(2) -> 2
- f(3) -> 3
- f(4) -> 4
- f(5) -> 5
- f(6) -> 6
- f(7) -> 7
- f(8) -> 8
- f(9) -> 9
【讨论】:
因为我---无聊---疯狂又喜欢挑战,所以做了个示范:Live On Coliru 改进/简化版本:Live On Coliru。有趣的是,调试输出从 2.3m 减少到 8k 行,不到 4‰ :) 当然输出相同【参考方案2】:std::string
已经是一个容器,你可以循环它:
std::string eq = "2x^2-1x";
for (char c : eq)
// use c
【讨论】:
【参考方案3】:您所说的是词法分析器。 Linux 中有一个(微软现在也使用它)称为lex
或flex
(它确实来自 GNU)。它有一个 C++ 扩展。这将负责解析。
从我在您的示例中可以看到:
[0-9]+ number
[a-z] letter
. operator
一旦你有了词法分析器,你就需要一个编译器。这就是 yacc
的用武之地。同样,有一个带有 C++ 扩展名,它被称为 Bison(来自 GNU)。
yacc
允许您编写您的解析,无论它可能变得多么复杂。 LALR 解析器存在限制 - Look Ahead Left-to-right Rightmost(派生),尽管 Bison 也为您提供 GLR 支持 - Generalized Left-to-right Rightmost(派生) em>。
为什么yacc
比编写自己的 C/C++ 代码更容易使用?因为它会优先考虑,而您不必做任何工作。当您编写a + b * c
时,您知道您首先需要计算b * c
,然后将a
添加到该产品中。自己写代码就没那么容易了(知道怎么写也没那么难)
yacc
的规则看起来有点像这样(没有必要的代码):
start: expr
expr: expr '+' expr
| expr '-' expr
| expr '*' expr
| expr '/' expr
| expr '^' expr
| '+' expr
| '-' expr
| '(' expr ')'
您必须在某个地方定义每个运算符的优先级。 '+'
和 '-
' 在这里最低,然后是 '*'
和 '/'
,然后是 '^'
。 (另外'^'
有另一个问题,它是“先计算右手边”,所以3^2^4
相当于3^(2^4)
,但我不会详细说明。)
一些附加信息和整个此类项目的实际示例:
https://en.wikipedia.org/wiki/GNU_bison
【讨论】:
实心帖子。我认为 OP 得到的比这里应得的多一点,但它是高质量的信息。我喜欢它。我还在学习:)【参考方案4】:std::string
以数组的形式提供对其内容的访问:
const string::size_type n=eq.length();
for(string::size_type i=0; i<n; ++i)
do_something(eq[i]);
// or see erenon's range-for example
但是,请注意parsing(分析字符串以理解方程式)很快就会变得非常复杂;到目前为止,数组访问是最容易的部分。
【讨论】:
以上是关于如何在 C++ 中将字符串拆分为数组的主要内容,如果未能解决你的问题,请参考以下文章
如何在 c++ 中将字符串拆分为左括号和右括号之间的字符串列表?