【问题标题】:How to parse math expressions in C#? [duplicate]如何在 C# 中解析数学表达式? [复制]
【发布时间】:2010-05-18 00:30:58
【问题描述】:

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

C#能否将y=3*x + 3等数学表达式解析成字符串?如果是这样,嗬? 我感谢您的帮助。

【问题讨论】:

  • 您必须提供更多信息:您想对表达式做什么?你想计算给定某个“x”的“y”的值吗?你想绘制方程图吗?还有什么?
  • “X 语言能做 Y”的问题非常愚蠢,因为答案几乎总是(至少)“如果你写代码就能做到!”

标签: c# math parsing expression


【解决方案1】:

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

这是 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");
    }
}

【讨论】:

  • 你能把完整的程序上传到哪里吗?提前致谢
  • @Ananth 我在五年前发表了这篇文章,所以代码早已不复存在。不过,完成起来非常简单,特别是如果您查看 cmets 中所述的 Shutting Yard 算法。要求人们提供完整的解决方案也超出了 SO 的范围,所以如果遇到麻烦,请先尝试并提出问题。
  • 在哪里可以找到写在代码sn-p顶部的Operand
【解决方案2】:

可能是以下内容的副本:

Is there a string math evaluator in .NET?

简短答案是否定的,详细答案请参见链接。 (我推荐 'coppercoders' 解决方案。)

【讨论】:

    【解决方案3】:

    你为什么不使用简单数学解析器或者其他东西是一样的? link text

    【讨论】:

      【解决方案4】:

      我在最近的 Silverlight 应用程序中采用了一种廉价的方法,即使用正则表达式(为了安全)擦洗字符串并将其传递给 JavaScript 求值器。它实际上工作得很好,但我承认这是一个 hack。

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

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-25
      • 1970-01-01
      相关资源
      最近更新 更多