一个简单的 C# 算术表达式 Eval 解析器 MathEvalor

发布于:2024-06-11 ⋅ 阅读:(151) ⋅ 点赞:(0)

一、功能

该类实现了简单的算术表达式解析和计算,支持 ±*/%^ () 运算符。

二、先看效果

[TestMethod()]
public void EvalTest()
{
    Assert.AreEqual(MathEvalor.Eval<double>("-12 * ( - 2.2 + 7.7 ) - 44 * 2"), -154d);
    Assert.AreEqual(MathEvalor.Eval<double>("12 % (1+4)"), 2);
    Assert.AreEqual(MathEvalor.Eval<double>("3^(1+1)"), 9);
}

二、代码

代码逻辑很简单,用一个操作符堆栈和一个值堆栈实现。先依个解析表达式,遇到数字就丢到值堆栈,遇到操作符就判定,是该计算还是出入堆栈处理。等所有操作符都出栈计算完毕后,值堆栈里面的值就是表达式的值。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace App.CodeDoms
{
    /// <summary>
    /// 简单的数学运算解析器。支持 +-*/%^运算。
    /// 更复杂的支持常量、布尔值、函数的请查看 MathEval.NET 项目.
    /// </summary>
    public class MathEvalor
    {
        /// <summary>简单数学运算表达式计算</summary>
        public static T Eval<T>(string expression) where T : struct
        {
            return (T)Eval(expression);
        }

        /// <summary>简单数学运算表达式计算</summary>
        public static object Eval(string expression)
        {
            expression = expression.Replace(" ", "") + "#";
            Stack numbers = new Stack();
            Stack operators = new Stack();
            operators.Push('#');

            int i = 0;
            var words = Regex.Matches(expression, @"(((?<=(^|\())-)?\d+(\.\d+)?|\D)");
            var word  = Convert.ToString(words[i++]);
            while (word != "#" || operators.Peek().ToString() != "#")
            {
                if ("+-*/()%^#".Contains(word))
                {
                    // 当前文本是操作符
                    switch (Judge(operators.Peek().ToString(), word))
                    {
                        case JudgeOp.Push:
                            operators.Push(word);
                            word = Convert.ToString(words[i++]);
                            break;
                        case JudgeOp.Pop:
                            operators.Pop();
                            word = Convert.ToString(words[i++]);
                            break;
                        case JudgeOp.Calc:
                            string o = Convert.ToString(operators.Pop());
                            double b = Convert.ToDouble(numbers.Pop());
                            double a = Convert.ToDouble(numbers.Pop());
                            numbers.Push(Operate(o, a, b));
                            break;
                        default:
                            return "Error";
                    }
                }
                else
                {
                    // 当前文本是数字
                    numbers.Push(word);
                    word = Convert.ToString(words[i++]);
                }
            }
            return numbers.Pop();
        }

        /// <summary>判定结果</summary>
        enum JudgeOp
        {
            Push,    // 操作符入栈
            Pop,     // 操作符出栈
            Calc,    // 计算
            Error    // 错误
        }

        /// <summary>判定操作是入栈、出栈、计算</summary>
        /// <param name="op">操作符</param>
        /// <param name="next">后继文本</param>
        static JudgeOp Judge(string op, string next)
        {
            switch (op)
            {
                case "+":
                case "-":
                    return "*/%^(".Contains(next) ? JudgeOp.Push : JudgeOp.Calc;
                case "*":
                case "/":
                case "%":
                case "^":
                    return (next == "(") ? JudgeOp.Push  : JudgeOp.Calc;
                case "(":
                    return (next == ")") ? JudgeOp.Pop   : JudgeOp.Push;
                case ")":
                    return (next == "(") ? JudgeOp.Error : JudgeOp.Calc;
                case "#":
                    return (next == "#") ? JudgeOp.Pop   : JudgeOp.Push;
            }
            return JudgeOp.Error;
        }

        /// <summary>二元操作</summary>
        /// <param name="op">二元操作符</param>
        static double Operate(string op, double a, double b)
        {
            switch (op)
            {
                case "+": return a + b;
                case "-": return a - b;
                case "*": return a * b;
                case "/": return a / b;
                case "%": return a % b;
                case "^": return Math.Pow(a, b);
            }
            return 0;
        }
    }
}

三、扩展

更高级的表达式解析还可能包括以下内容:

  • 常量
  • 布尔值
  • 一元操作服、三元操作符
  • 函数:字符串函数、数学函数、日期时间、逻辑函数等
  • 类对象及属性
  • 扩展方法
  • Linq

可参考以下项目:


网站公告

今日签到

点亮在社区的每一天
去签到