如何在C ++中将字符串拆分为数组

时间:2017-09-17 19:57:36

标签: c++ arrays string input split

我需要捕捉一个等式(例如2x ^ 2-1x)我有这段代码来捕获它

string eq;
cin>>eq;

为了解决这个问题,我需要将前一个字符串的每个字符拆分成一个数组,然后用循环来解决它。我想我知道如何做循环部分,但我怎么能把它分成一个数组呢?或者有更简单的方法来做到这一点吗?

4 个答案:

答案 0 :(得分:4)

您实际上并没有尝试将字符串拆分为数组。一个阵列什么都不买你。你想要一个表达式树。或者至少是一个动态评估的递归下降解析。这会更容易但效率更低。

StackOverflow上的递归下降表达式解析器必须有很多问题/答案。使用搜索框获取想法。

样本

为了完全矫枉过正,这里有一个公式评估函数的样本,包含动态(单字母)变量和一些测试用例。

它使用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

答案 1 :(得分:3)

std::string已经是一个容器,你可以循环它:

std::string eq = "2x^2-1x";
for (char c : eq)
{
  // use c
}

答案 2 :(得分:2)

你所说的是一个词法分析者。 Linux中有一个(微软现在也使用它)称为lexflex(它实际上来自GNU)。它有一个C ++扩展。这将解析解析。

从我在你的例子中看到的内容:

[0-9]+      number
[a-z]       letter
.           operator

一旦你有了词法分析器,你需要一个编译器。这就是yacc所在的位置。再次,有一个带有C ++扩展名,它被称为Bison(来自GNU)。

yacc允许您编写几乎无论多么复杂的解析。 LALR解析器有限制 - 向前看从左到右最右边(派生),虽然Bison也为你提供GLR支持 - 广义从左到右最右边(派生)

为什么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 ')'

在某个地方,您必须定义每个运营商的优先级。 '+''-&#39;这里最低,然后是'*''/',然后是'^'。 (此外'^'还有另一个问题,它是&#34;先计算右侧&#34;,因此3^2^4相当于3^(2^4)但是我不知道这些细节。)

一些额外的信息以及整个此类项目的实际示例:

https://en.wikipedia.org/wiki/GNU_bison

答案 3 :(得分:1)

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(分析字符串以理解方程式)会非常迅速地变得非常复杂;数组访问是最简单的部分。