在C#中实现简单计算器功能的核心是正确解析用户输入的数学表达式字符串,比如处理"3+5*2"这类包含不同优先级运算符的表达式,需要按照运算规则拆分计算,而不是从左到右直接累加。

核心实现思路
数学表达式解析的常用方案是使用双栈结构,一个栈存储数字,一个栈存储运算符,遍历表达式字符串时按照规则入栈、出栈计算,具体规则如下:
- 遇到数字时,提取完整的连续数字(支持多位数)
- 遇到运算符时,比较当前运算符和栈顶运算符的优先级,栈顶优先级更高则先计算栈顶的运算
- 表达式遍历完成后,将栈中剩余的运算符依次计算
- 括号作为特殊运算符处理,左括号直接入栈,遇到右括号时计算到左括号为止
优先级定义
我们先定义运算符的优先级,加减优先级为1,乘除优先级为2,括号单独处理:
// 定义运算符优先级
private static int GetPriority(char op)
{
switch (op)
{
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return 0;
}
}
完整计算器实现代码
下面是完整的表达式解析和计算代码,支持加减乘除和括号运算:
using System;
using System.Collections.Generic;
public class SimpleCalculator
{
// 判断字符是否为运算符
private static bool IsOperator(char c)
{
return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')';
}
// 获取运算符优先级
private static int GetPriority(char op)
{
switch (op)
{
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return 0;
}
}
// 执行单次运算
private static double Calculate(double num1, double num2, char op)
{
switch (op)
{
case '+':
return num1 + num2;
case '-':
return num1 - num2;
case '*':
return num1 * num2;
case '/':
if (num2 == 0)
{
throw new DivideByZeroException("除数不能为0");
}
return num1 / num2;
default:
return 0;
}
}
// 解析并计算数学表达式
public static double CalculateExpression(string expression)
{
// 数字栈
Stack<double> numStack = new Stack<double>();
// 运算符栈
Stack<char> opStack = new Stack<char>();
int i = 0;
while (i < expression.Length)
{
char c = expression[i];
// 跳过空格
if (c == ' ')
{
i++;
continue;
}
// 处理数字
if (char.IsDigit(c) || c == '.')
{
string numStr = "";
// 提取完整的数字(支持小数)
while (i < expression.Length && (char.IsDigit(expression[i]) || expression[i] == '.'))
{
numStr += expression[i];
i++;
}
if (double.TryParse(numStr, out double num))
{
numStack.Push(num);
}
continue;
}
// 处理左括号
if (c == '(')
{
opStack.Push(c);
i++;
}
// 处理右括号
else if (c == ')')
{
// 计算到左括号为止
while (opStack.Peek() != '(')
{
char op = opStack.Pop();
double num2 = numStack.Pop();
double num1 = numStack.Pop();
numStack.Push(Calculate(num1, num2, op));
}
// 弹出左括号
opStack.Pop();
i++;
}
// 处理运算符
else if (IsOperator(c))
{
// 比较优先级,栈顶运算符优先级更高则先计算
while (opStack.Count > 0 && GetPriority(opStack.Peek()) >= GetPriority(c))
{
char op = opStack.Pop();
double num2 = numStack.Pop();
double num1 = numStack.Pop();
numStack.Push(Calculate(num1, num2, op));
}
opStack.Push(c);
i++;
}
else
{
throw new ArgumentException($"表达式包含非法字符: {c}");
}
}
// 处理栈中剩余的运算
while (opStack.Count > 0)
{
char op = opStack.Pop();
double num2 = numStack.Pop();
double num1 = numStack.Pop();
numStack.Push(Calculate(num1, num2, op));
}
return numStack.Pop();
}
}
使用示例
调用上面的计算器类进行计算:
class Program
{
static void Main(string[] args)
{
string expression1 = "3+5*2";
string expression2 = "(3+5)*2";
string expression3 = "10-2*3+8/4";
Console.WriteLine($"{expression1} = {SimpleCalculator.CalculateExpression(expression1)}");
Console.WriteLine($"{expression2} = {SimpleCalculator.CalculateExpression(expression2)}");
Console.WriteLine($"{expression3} = {SimpleCalculator.CalculateExpression(expression3)}");
}
}
上述代码运行后会输出正确结果,分别是13、16、6,符合数学运算规则。
注意事项
- 当前实现仅支持非负数字,若需要支持负数,可以在表达式开头或左括号后添加负号处理逻辑
- 代码中没有处理除数为0的情况,实际使用时可以根据需求添加更友好的异常提示
- 如果表达式格式错误(比如括号不匹配),会抛出异常,实际项目中可以添加表达式合法性校验逻辑