C#后缀表达式解析计算字符串公式 - 以往清泉
source link: https://www.cnblogs.com/xwc1996/p/17143880.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
当我们拿到一个字符串比如:20+31*(100+1)
的时候用口算就能算出结果为3151
,因为这是中缀表达式
对于人类的思维很简单,但是对于计算机就比较复杂了。相对的后缀表达式
适合计算机进行计算。
我们就从简单到复杂,逐步实现对公式的解析(下述的代码没有经过严格验证,可能会存在极端情况的BUG,作为一种思路仅供参考,商用环境还需细细修改)。
实现简单的数字的加减乘除
我们从实现简单的数字的加减乘除开始主要是提供一个思路有需要可以自己修改扩展比如增加函数、字符串、数组等(推荐一个项目写的感觉就不错https://github.com/KovtunV/NoStringEvaluating),那么我们只需要关注加减乘除等操作符、左右括号和操作数(整数、小数和负数),所以我们先建立三个枚举类BracketEnum
、NodeTypeEnum
和OperatorEnum
如下:
BracketEnum
是括号枚举,也就是左右括号"()"
View Code
NodeTypeEnum
是节点类型枚举,就简单分为操作符、操作数和括号
View Code
OperatorEnum
是操作符枚举,主要就是加减乘除这些简单的
View Code
然后我们需要做以下三步:
- 解析公式将字符转化为便于操作的节点信息
- 进行解析为后缀表达式
1、解析公式转为节点信息
根据我们的NodeTypeEnum
节点类型枚举我们需要三个不同的节点信息类方便我们的操作,我们先创建基类BaseNode
以后的节点类都继承它
public class BaseNode { public BaseNode(NodeTypeEnum nodeType) { NodeType = nodeType; } /// <summary> /// 节点类型 /// </summary> public NodeTypeEnum NodeType { get; set; } }
然后我们分别创建BracketNode
、NumberNode
和OperatorNode
类,分别是括号节点信息、操作数节点新和操作符节点信息,它们各有自己的具体实现,如下:
View Code
View Code
View Code
有了节点信息类,那我们肯定还要有对应的解析类分别是BracketReader(括号解析)
、NumberReader(操作数解析)
和OperatorReader(操作符解析)
,解析类就是为了将公式字符串解析为对应的节点信息具体如下:
View Code
View Code
View Code
有了以上的准备,我们就可以将公式转为我们的节点信息了如下
/// <summary> /// 解析公式为节点 /// </summary> /// <param name="formula">公式字符串</param> /// <returns></returns> public static List<BaseNode> AnalysisFormulaToNodes(string formula) { var nodes = new List<BaseNode>(); for(var index = 0;index< formula.Length; index++) { if (NumberReader.TryProceedNumber(nodes, formula.AsSpan(), ref index)) continue; if (OperatorReader.TryProceedOperator(nodes, formula.AsSpan(), ref index)) continue; if (BracketReader.TryProceedOpenBracket(nodes, formula.AsSpan(), ref index)) continue; if (BracketReader.TryProceedCloseBracket(nodes, formula.AsSpan(), ref index)) continue; } return nodes; }
2、转为后缀表达式
转为后缀表达式需要执行以下条件:
View Code
3、计算后缀表达式
/// <summary> /// 计算后缀表达式 /// </summary> /// <param name="nodes"></param> /// <returns></returns> public static double CalculationRPN(List<BaseNode> nodes) { double result = 0; Stack<BaseNode> stack = new Stack<BaseNode>(); foreach(var t in nodes) { if(t.NodeType == NodeTypeEnum.Number) { //操作数直接入栈 stack.Push(t); } else if(t.NodeType == NodeTypeEnum.Operator) { //操作符弹出栈顶两个进行计算 var a = stack.Pop(); var b = stack.Pop(); var operate = t as OperatorNode; var value = operate.OperatorKey switch { // 数学操作符 OperatorEnum.Multiply => OperatorService.Multiply(a, b), OperatorEnum.Divide => OperatorService.Divide(a, b), OperatorEnum.Plus => OperatorService.Plus(a, b), OperatorEnum.Minus => OperatorService.Minus(a, b), OperatorEnum.Power => OperatorService.Power(a, b), }; stack.Push(new NumberNode(value)); } } result = (stack.Pop() as NumberNode).Number; return result; }
数学操作符执行代码如下主要为了进行加减乘除简单的计算:
View Code
最后串在一起就能得到结果啦,就像下面这样
/// <summary> /// 计算 /// </summary> /// <param name="formula">公式字符串</param> /// <returns></returns> public static double Calculation(string formula) { //1、获取公式节点 var nodes = AnalysisFormulaToNodes(formula); //2、转后缀表达式 var rpnNodes = GetRPN(nodes); //3、计算对后缀表达式求值 var result = CalculationRPN(rpnNodes); return result; }
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK