最省事办法是先用std::replace将运算符前后补空格,再用std::stringstream按空格分割token,对每个token用std::stod尝试转数字,失败则视为运算符(捕获std::invalid_argument)。

怎么用 std::string 和 std::stringstream 拆出加减乘除和数字
直接切分公式字符串最省事的办法,不是手写状态机,而是靠空格分隔 + 类型试探。但前提是公式里**必须有空格**,比如 "3 + 4 * 2" 可以,"3+4*2" 就会整个被当做一个 token 处理失败。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 先用
std::replace把所有运算符前后补上空格:"+"→" + ",再用std::stringstream按空格读取 - 每个 token 用
std::stod尝试转数字;失败就当作运算符处理(注意捕获std::invalid_argument) - 别依赖
operator>>直接读double,它遇到"+"会静默失败并置failbit,后续读取全乱
为什么手写字符扫描比正则更靠谱(尤其在 C++11/14 下)
C++ 标准库的 std::regex 在旧编译器上性能差、匹配行为不一致,甚至某些版本对空匹配处理异常——你写好一个 R"(\d+|\+|\-|\*|\/)",在 GCC 4.8 和 Clang 3.5 上结果可能不同。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 从头遍历
std::string::const_iterator,用std::isdigit判数字,用switch判运算符 - 数字要连续读:遇到
'1'后继续看下一个是不是数字,直到非数字为止,再用std::stod转整段 - 跳过空白用
std::isspace,别只判断' ',否则制表符或换行会让解析中断
std::vector<:any></:any> 或 std::variant<double char></double> 存 token 哪个更稳
用 std::any 看似灵活,但运行时类型检查开销大,且提取值时必须用 std::any_cast,一旦类型错就抛异常;而 std::variant 编译期约束强,配合 std::visit 安全又清晰——但要求 C++17。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 如果项目锁死 C++11/14,老实用两个平行容器:
std::vector<double> numbers</double>和std::vector<char> ops</char>,靠索引对齐 - 若可用 C++17,优先选
std::variant<double char></double>,定义 token 类型:using Token = std::variant<double char></double> - 避免把括号也塞进同一
std::variant——它和运算符语义不同,后期做语法分析时容易混淆
遇到负号 "-5" 和减号 "3 - 5" 怎么区分
这是最常翻车的地方:单靠字符没法判断,必须结合上下文。开头的 '-' 或左邻是运算符/括号的 '-' 是负号;左邻是数字或右括号的才是减号。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 扫描时记前一个 token 类型:如果是
double、')'或未开始(即位置 0),当前'-'是减号;否则是负号 - 负号要和后面数字合并成一个 token,比如
"-5"解析为-5.0,而不是两个 token:'-'和5 - 别在词法层尝试处理
"--5"或"+-3",这些属于语法错误,留给后续阶段报错
词法分析真正的难点不在拆字符,而在边界条件:空输入、连续运算符、科学计数法("1e-3")、十六进制前缀("0xFF")——这些不提前约定规则,光靠“简单”俩字撑不住。










