设计C++计算器需构建输入/输出、词法分析、语法解析、求值引擎和错误处理五大模块,通过分阶段处理实现表达式解析与计算。

C++实现一个简单计算器项目,核心在于将用户输入的数学表达式,通过一系列逻辑步骤,转换为计算机可以理解并执行的计算指令。这通常涉及表达式的解析、运算符优先级的处理,以及最终的数值计算。它不仅仅是简单的加减乘除,更是一次对字符串处理、数据结构和算法应用的综合实践,也是理解编译器或解释器基础原理的一个绝佳起点。
要构建一个能处理基本四则运算(加、减、乘、除)并考虑运算符优先级和括号的C++计算器,我通常会采取一种分阶段的处理方法:
词法分析(Tokenization):这是第一步,也是最直观的一步。我们需要将用户输入的原始字符串表达式(例如 "1 + 2 (3 - 4)")分解成一系列有意义的“词法单元”或“令牌”(Tokens)。这些令牌可以是数字、运算符(+,-,\,/)、括号等等。例如,"1" 是一个数字令牌,"+" 是一个运算符令牌。
enum TokenType { NUMBER, PLUS, MINUS, MULTIPLY, DIVIDE, LPAREN, RPAREN, END };
struct Token {
TokenType type;
double value; // For NUMBER tokens
char op; // For operator tokens
};
std::vector<Token> tokenize(const std::string& expression) {
std::vector<Token> tokens;
for (size_t i = 0; i < expression.length(); ++i) {
char c = expression[i];
if (isspace(c)) continue;
if (isdigit(c) || c == '.') {
std::string num_str;
while (i < expression.length() && (isdigit(expression[i]) || expression[i] == '.')) {
num_str += expression[i];
i++;
}
i--; // Adjust index after reading number
tokens.push_back({NUMBER, std::stod(num_str)});
} else if (c == '+') tokens.push_back({PLUS, 0, '+'});
else if (c == '-') tokens.push_back({MINUS, 0, '-'});
else if (c == '*') tokens.push_back({MULTIPLY, 0, '*'});
else if (c == '/') tokens.push_back({DIVIDE, 0, '/'});
else if (c == '(') tokens.push_back({LPAREN, 0, '('});
else if (c == ')') tokens.push_back({RPAREN, 0, ')'});
else {
// 错误处理:未知字符
throw std::runtime_error("Invalid character in expression: " + std::string(1, c));
}
}
tokens.push_back({END}); // 标记表达式结束
return tokens;
}语法分析与中缀转后缀(Shunting-yard Algorithm):这是处理运算符优先级和括号的关键。我们将中缀表达式(人类习惯的写法)转换为后缀表达式(逆波兰表示法,RPN)。后缀表达式的优点在于,它不需要括号来表示优先级,计算起来非常直接,只需一个栈。
立即学习“C++免费学习笔记(深入)”;
// 辅助函数:获取运算符优先级
int get_precedence(char op) {
if (op == '+' || op == '-') return 1;
if (op == '*' || op == '/') return 2;
return 0; // For parentheses or unknown
}
std::vector<Token> infix_to_postfix(const std::vector<Token>& infix_tokens) {
std::vector<Token> postfix_tokens;
std::stack<Token> op_stack;
for (const auto& token : infix_tokens) {
if (token.type == NUMBER) {
postfix_tokens.push_back(token);
} else if (token.type == LPAREN) {
op_stack.push(token);
} else if (token.type == RPAREN) {
while (!op_stack.empty() && op_stack.top().type != LPAREN) {
postfix_tokens.push_back(op_stack.top());
op_stack.pop();
}
if (op_stack.empty() || op_stack.top().type != LPAREN) {
throw std::runtime_error("Mismatched parentheses.");
}
op_stack.pop(); // Pop the LPAREN
} else if (token.type == PLUS || token.type == MINUS || token.type == MULTIPLY || token.type == DIVIDE) {
while (!op_stack.empty() && op_stack.top().type != LPAREN &&
get_precedence(op_stack.top().op) >= get_precedence(token.op)) {
postfix_tokens.push_back(op_stack.top());
op_stack.pop();
}
op_stack.push(token);
}
}
while (!op_stack.empty()) {
if (op_stack.top().type == LPAREN) {
throw std::runtime_error("Mismatched parentheses.");
}
postfix_tokens.push_back(op_stack.top());
op_stack.pop();
}
return postfix_tokens;
}后缀表达式求值:现在我们有了后缀表达式,求值就变得简单了。
double evaluate_postfix(const std::vector<Token>& postfix_tokens) {
std::stack<double> operand_stack;
for (const auto& token : postfix_tokens) {
if (token.type == NUMBER) {
operand_stack.push(token.value);
} else { // Operator
if (operand_stack.size() < 2) {
throw std::runtime_error("Invalid expression: not enough operands for operator.");
}
double op2 = operand_stack.top(); operand_stack.pop();
double op1 = operand_stack.top(); operand_stack.pop();
double result;
if (token.op == '+') result = op1 + op2;
else if (token.op == '-') result = op1 - op2;
else if (token.op == '*') result = op1 * op2;
else if (token.op == '/') {
if (op2 == 0) throw std::runtime_error("Division by zero.");
result = op1 / op2;
}
operand_stack.push(result);
}
}
if (operand_stack.size() != 1) {
throw std::runtime_error("Invalid expression: too many operands or operators.");
}
return operand_stack.top();
}主函数集成与错误处理:将上述步骤整合起来,并加入适当的错误捕获。
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <stdexcept> // For std::runtime_error
#include <cctype> // For isspace, isdigit
// ... (TokenType, Token struct, tokenize, get_precedence, infix_to_postfix, evaluate_postfix functions here) ...
int main() {
std::string expression;
std::cout << "Enter an expression (e.g., 1 + 2 * (3 - 4)): ";
std::getline(std::cin, expression);
try {
std::vector<Token> tokens = tokenize(expression);
// Optional: print tokens for debugging
// for(const auto& t : tokens) { /* print token info */ }
std::vector<Token> postfix_tokens = infix_to_postfix(tokens);
// Optional: print postfix tokens for debugging
// for(const auto& t : postfix_tokens) { /* print token info */ }
double result = evaluate_postfix(postfix_tokens);
std::cout << "Result: " << result << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
} catch (...) {
std::cerr << "An unknown error occurred." << std::endl;
}
return 0;
}设计一个C++计算器,无论简单与否,其背后都隐含着几个关键的逻辑模块,它们协同工作,将用户输入的字符串转化为最终的计算结果。在我看来,这几个模块是:
输入/输出模块(I/O Handler):
词法分析器(Lexer / Tokenizer):
语法分析器 / 表达式解析器(Parser / Expression Evaluator):
求值引擎(Evaluation Engine):
错误处理模块(Error Handler):
这些模块虽然各自独立,但在实际项目中它们紧密相连,形成一个数据流动的管道。理解它们的职责和相互关系,是成功构建计算器的基础。
处理运算符优先级和括号是计算器项目中最核心也最容易出错的部分。我个人经验中,最优雅且广泛采用的解决方案是Shunting-yard算法(调度场算法),它能将中缀表达式(我们日常书写的形式,如
A + B * C
A B C * +
Shunting-yard算法的核心思想:
这个算法通过使用两个栈来完成转换:
具体处理步骤和原理:
遍历中缀表达式的词法单元(Token),根据Token的类型执行不同操作:
数字(Operand):
*运算符(Operator,如 +
-
,
A + B * C
*
+
*
+
*
*
+
A B C * +
左括号 (
右括号 )
(
表达式遍历结束:
后缀表达式求值:
一旦有了后缀表达式,求值就非常直观了,只需要一个操作数栈:
遍历后缀表达式的Token:
求值结束:
这种方法在处理复杂表达式时非常强大和可靠,是构建任何具有优先级和括号功能的计算器的基石。它将语法分析和求值逻辑清晰地分离开来,使得代码更易于理解和维护。
在C++计算器项目中,错误处理是确保程序健壮性和用户体验的关键一环。我通常会从几个层面去考虑和实现错误处理:
输入验证与非法字符:
tokenize
std::runtime_error
// 在 tokenize 函数中
else {
throw std::runtime_error("Invalid character in expression: " + std::string(1, c));
}语法错误(Mismatched Parentheses):
(1 + 2
1 + 2)
// 在 infix_to_postfix 函数中
if (op_stack.empty() || op_stack.top().type != LPAREN) {
throw std::runtime_error("Mismatched parentheses: missing opening parenthesis.");
}
// ...
while (!op_stack.empty()) {
if (op_stack.top().type == LPAREN) {
throw std::runtime_error("Mismatched parentheses: missing closing parenthesis.");
}
// ...
}以上就是C++如何实现简单计算器项目的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号