用双栈法解析字符串"2 + 3 4"为整数14:操作数栈存数字,运算符栈按优先级延迟计算,遇 / 先弹出高优先级运算,末尾补算;正确处理负号(开头、括号后、运算符后视为一元)、括号(递归下降)和多位数。

怎么把字符串 "2 + 3 * 4" 变成整数 14
核心是写一个能识别数字、运算符和优先级的解析器,不是调 std::eval(C++ 根本没有这个函数),也不是硬编码几个 case。得从左到右扫描,用两个栈或递归下降——推荐双栈法,简单可控,适合“简单公式”场景。
常见错误:直接按顺序计算 "2 + 3 * 4" 得到 20(先算 2+3),忽略乘除优先级;或者没处理负号 "-5 + 3",把开头的 - 当减号导致解析失败。
- 用一个栈存操作数(
std::stack),另一个存运算符(std::stack) - 遇到数字,完整读取(支持多位、负数),压入操作数栈
- 遇到运算符时,检查栈顶是否有更高或同级优先级的运算符(
*和/优先级高于+和-),有就先弹出并计算 - 表达式末尾要额外触发一次运算,别漏掉最后一个操作数后的运算
如何安全处理负号和括号
负号不是二元减号,是单目运算符,位置很关键:在表达式开头、左括号后、或前一个字符是运算符时,才可能是负号。括号则靠递归或栈配对解决——但“简单公式”里建议用递归下降,比手动管理括号栈更清晰。
典型坑:"(2 + -3) * 4" 中的 -3,如果只靠空格分词会卡在 "-3" 被当整体数字;而 "2 + (-3)" 的左括号后紧跟 -,必须识别为一元负号。
立即学习“C++免费学习笔记(深入)”;
- 扫描时记前一个 token 类型:若前一个是
'('或运算符,当前'-'就按一元负号处理,生成0 - x等价形式 - 括号用递归:遇到
'('就递归调用解析函数,返回值和匹配的')'位置,主流程跳过这段 - 不要试图用正则切分表达式——
"12.3"、"-45"、"(-2)"形态太杂,手写 scanner 更稳
要不要支持浮点数和变量?先看需求边界
如果只是“简单数学公式”,比如计算器输入、配置里的阈值表达式(如 "max(10, a * 0.8 + 2)"),那浮点支持值得加;但变量需要符号表,复杂度跳变。先跑通整数版,再扩展。
加浮点的关键改动点不多,但容易崩:字符串转浮点要用 std::stod 而非 std::stoi;运算栈类型得换成 double;乘除精度问题不用管,但比较相等时别用 ==;另外 "1e-3" 这类科学计数法,stod 能直接吃,不用额外解析。
- 数字解析函数统一返回
std::optional,失败时抛异常或返回错误码 - 如果后续要支持函数调用(如
sin(3.14)),就在 token 匹配到标识符后判断是否为函数名,再解析括号内参数 - 变量支持=引入 map
,但首次访问未定义变量时必须报错,不能静默当 0
为什么不用现成库比如 muParser 或 exprtk
它们确实健壮,但依赖外部头文件、编译选项敏感、错误提示不透明。如果你只是嵌入一段轻量逻辑(比如游戏里解析伤害公式 "level * (atk + 2) / 10"),自己写 200 行以内可维护的解析器反而更快定位问题。
真正容易被忽略的是错误恢复:用户输错 "2 ++ 3" 或漏括号,你的解析器不能直接 abort,至少要告诉第几列出错。加个 size_t pos 参数一路传下去,配合 std::string_view 切片,就能准确定位。
写完后拿这几个测: "-2 * (-3 + 4)"、"10 / 3"(确认是浮点还是整除)、"((1))" —— 边界越全,后面改需求时越省心。











