如何在 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的形式,你需要解析abc的值? 在 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 中有一个(微软现在也使用它)称为lexflex(它确实来自 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++ 中将空格分隔的字符串拆分为多个字符串?

如何在awk中将分隔字符串拆分为数组?

如何在 c++ 中将字符串拆分为左括号和右括号之间的字符串列表?

在c ++中将单词拆分为字母? [关闭]

关于如何在 Swift 中将字符串拆分为所需字符串数组的问题

你如何在 C++ 中拆分数组?