如何在C#中解析数学表达式?

时间:2010-05-18 00:30:58

标签: c# math parsing expression

  

可能重复:
  Is there a string math evaluator in .NET?

C#可以将数学表达式(如y = 3 * x + 3)解析为字符串吗?如果是的话,好吗? 我感谢您的帮助。

4 个答案:

答案 0 :(得分:5)

这是我前一段时间用来解析中缀(运算符操作数运算符)方程的一些代码。 有一些小类和辅助函数缺失,但实现它们应该相当容易。如果您需要它们或任何帮助,请告诉我,我可以将它们上传到某个地方。

这是Dijkstra Shunting-yard algorithm

的基本实现
public Operand ExpressionTree
{
    get;
    private set;
}

private Stack<Operands.Operand> stack = new Stack<InfixParser.Operands.Operand>();
private Queue<Operands.Operand> outputQueue = new Queue<InfixParser.Operands.Operand>();

private void ParseFormulaString()
{
    //Dijkstra's Shunting Yard Algorithm
    Regex re = new Regex(@"([\+\-\*\(\)\^\/\ ])");
    List<String> tokenList = re.Split(formulaString).Select(t => t.Trim()).Where(t => t != "").ToList();

    for (int tokenNumber = 0; tokenNumber < tokenList.Count(); ++tokenNumber)
    {
        String token = tokenList[tokenNumber];
        TokenClass tokenClass = GetTokenClass(token);

        switch (tokenClass)
        {
            case TokenClass.Value:
                outputQueue.Enqueue(new Value(token));
                break;
            case TokenClass.Function:
                stack.Push(new Function(token, 1));
                break;
            case TokenClass.Operator:
                if (token == "-" && (stack.Count == 0 || tokenList[tokenNumber - 1] == "("))
                {
                    //Push unary operator 'Negative' to stack
                    stack.Push(new Negative());
                    break;
                }
                if (stack.Count > 0)
                {
                    String stackTopToken = stack.Peek().Token;
                    if (GetTokenClass(stackTopToken) == TokenClass.Operator)
                    {
                        Associativity tokenAssociativity = GetOperatorAssociativity(token);
                        int tokenPrecedence = GetOperatorPrecedence(token);
                        int stackTopPrecedence = GetOperatorPrecedence(stackTopToken);

                        if (tokenAssociativity == Associativity.Left && tokenPrecedence <= stackTopPrecedence ||
                            tokenAssociativity == Associativity.Right && tokenPrecedence < stackTopPrecedence)
                        {
                            outputQueue.Enqueue(stack.Pop());
                        }
                    }
                }
                stack.Push(new BinaryOperator(token, Operator.OperatorNotation.Infix));
                break;
            case TokenClass.LeftParen:
                stack.Push(new LeftParenthesis());
                break;
            case TokenClass.RightParen:
                while (!(stack.Peek() is LeftParenthesis))
                {
                    outputQueue.Enqueue(stack.Pop());
                }
                stack.Pop();

                if (stack.Count > 0 && stack.Peek() is Function)
                {
                    outputQueue.Enqueue(stack.Pop());
                }
                break;
        }

        if (tokenClass == TokenClass.Value || tokenClass == TokenClass.RightParen)
        {
            if (tokenNumber < tokenList.Count() - 1)
            {
                String nextToken = tokenList[tokenNumber + 1];
                TokenClass nextTokenClass = GetTokenClass(nextToken);
                if (nextTokenClass != TokenClass.Operator && nextTokenClass != TokenClass.RightParen)
                {
                    tokenList.Insert(tokenNumber + 1, "*");
                }
            }
        }
    }

    while (stack.Count > 0)
    {
        Operand operand = stack.Pop();
        if (operand is LeftParenthesis || operand is RightParenthesis)
        {
            throw new ArgumentException("Mismatched parentheses");
        }

        outputQueue.Enqueue(operand);
    }

    String foo = String.Join(",", outputQueue.Select(t => t.Token).ToArray());
    String bar = String.Join("", tokenList.ToArray());

    Stack<Operand> expressionStack = new Stack<Operand>();
    while (outputQueue.Count > 0)
    {
        Operand operand = outputQueue.Dequeue();

        if (operand is Value)
        {
            expressionStack.Push(operand);
        }
        else
        {
            if (operand is BinaryOperator)
            {
                BinaryOperator op = (BinaryOperator)operand;
                Operand rightOperand = expressionStack.Pop();
                Operand leftOperand = expressionStack.Pop();
                op.LeftOperand = leftOperand;
                op.RightOperand = rightOperand;
            }
            else if (operand is UnaryOperator)
            {
                ((UnaryOperator)operand).Operand = expressionStack.Pop();
            }
            else if (operand is Function)
            {
                Function function = (Function)operand;
                for (int argNum = 0; argNum < function.NumArguments; ++argNum)
                {
                    function.Arguments.Add(expressionStack.Pop());
                }
            }

            expressionStack.Push(operand);
        }
    }

    if (expressionStack.Count != 1)
    {
        throw new ArgumentException("Invalid formula");
    }

    ExpressionTree = expressionStack.Pop();
}

private TokenClass GetTokenClass(String token)
{
    double tempValue;
    if (double.TryParse(token, out tempValue) ||
        token.Equals("R", StringComparison.CurrentCultureIgnoreCase) ||
        token.Equals("S", StringComparison.CurrentCultureIgnoreCase))
    {
        return TokenClass.Value;
    }
    else if (token.Equals("sqrt", StringComparison.CurrentCultureIgnoreCase))
    {
        return TokenClass.Function;
    }
    else if (token == "(")
    {
        return TokenClass.LeftParen;
    }
    else if (token == ")")
    {
        return TokenClass.RightParen;
    }
    else if (binaryInfixOperators.Contains(token))
    {
        return TokenClass.Operator;
    }
    else
    {
        throw new ArgumentException("Invalid token");
    }
}

private Associativity GetOperatorAssociativity(String token)
{
    if (token == "^")
        return Associativity.Right;
    else
        return Associativity.Left;
}

private int GetOperatorPrecedence(String token)
{
    if (token == "+" || token == "-")
    {
        return 1;
    }
    else if (token == "*" || token == "/")
    {
        return 2;
    }
    else if (token == "^")
    {
        return 3;
    }
    else
    {
        throw new ArgumentException("Invalid token");
    }
}

答案 1 :(得分:2)

可能是以下的副本:

Is there a string math evaluator in .NET?

简短的回答是否定的,长期回答请看链接。 (我推荐'铜码'解决方案。)

答案 2 :(得分:0)

为什么不使用Simple Math Parser或者同样的东西? link text

答案 3 :(得分:0)

我在最近的Silverlight应用程序中通过使用正则表达式(为安全起见)擦除字符串并将其传递给JavaScript评估程序来获得廉价的方法。它实际上运作得很好,但这是我承认的黑客攻击。

http://josheinstein.com/blog/index.php/2010/03/mathevalconverter