如何以编程方式将字符串转换为数学表达式

问题描述 投票:0回答:7

我是 C# 初学者。我在将字符串转换为数学表达式时遇到问题。我有一个用户界面,用户可以使用随机公式字段创建公式。在另一个用户界面中,用户将输入这些公式字段。

例如,第一次的公式可能是

(a+b)^n
,而另一个公式可能是
((a+b+c)^n+b)

在我的计算 UI 中,第一次用户将输入 a、b、n,第二个公式用户将输入 a、b、c、n。谁能帮助我了解如何以编程方式获得两个公式的结果?预先感谢

c# math expression formula
7个回答
6
投票

应该这样做:

public class StringToFormula
{
    private string[] _operators = { "-", "+", "/", "*","^"};
    private  Func<double, double, double>[] _operations = {
        (a1, a2) => a1 - a2,
        (a1, a2) => a1 + a2,
        (a1, a2) => a1 / a2,
        (a1, a2) => a1 * a2,
        (a1, a2) => Math.Pow(a1, a2)
    };

    public double Eval(string expression)
    {
        List<string> tokens = getTokens(expression);
        Stack<double> operandStack = new Stack<double>();
        Stack<string> operatorStack = new Stack<string>();
        int tokenIndex = 0;

        while (tokenIndex < tokens.Count) {
            string token = tokens[tokenIndex];
            if (token == "(") {
                string subExpr = getSubExpression(tokens, ref tokenIndex);
                operandStack.Push(Eval(subExpr));
                continue;
            }
            if (token == ")") {
                throw new ArgumentException("Mis-matched parentheses in expression");
            }
            //If this is an operator  
            if (Array.IndexOf(_operators, token) >= 0) {
                while (operatorStack.Count > 0 && Array.IndexOf(_operators, token) < Array.IndexOf(_operators, operatorStack.Peek())) {
                    string op = operatorStack.Pop();
                    double arg2 = operandStack.Pop();
                    double arg1 = operandStack.Pop();
                    operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
                }
                operatorStack.Push(token);
            } else {
                operandStack.Push(double.Parse(token));
            }
            tokenIndex += 1;
        }

        while (operatorStack.Count > 0) {
            string op = operatorStack.Pop();
            double arg2 = operandStack.Pop();
            double arg1 = operandStack.Pop();
            operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
        }
        return operandStack.Pop();
    }

    private string getSubExpression(List<string> tokens, ref int index)
    {
        StringBuilder subExpr = new StringBuilder();
        int parenlevels = 1;
        index += 1;
        while (index < tokens.Count && parenlevels > 0) {
            string token = tokens[index];
            if (tokens[index] == "(") {
                parenlevels += 1;
            }

            if (tokens[index] == ")") {
                parenlevels -= 1;
            }

            if (parenlevels > 0) {
                subExpr.Append(token);
            }

            index += 1;
        }

        if ((parenlevels > 0)) {
            throw new ArgumentException("Mis-matched parentheses in expression");
        }
        return subExpr.ToString();
    }

    private List<string> getTokens(string expression)
    {
        string operators = "()^*/+-";
        List<string> tokens = new List<string>();
        StringBuilder sb = new StringBuilder();

        foreach (char c in expression.Replace(" ", string.Empty)) {
            if (operators.IndexOf(c) >= 0) {
                if ((sb.Length > 0)) {
                    tokens.Add(sb.ToString());
                    sb.Length = 0;
                }
                tokens.Add(c);
            } else {
                sb.Append(c);
            }
        }

        if ((sb.Length > 0)) {
            tokens.Add(sb.ToString());
        }
        return tokens;
    }
}

像这样调用类和方法:

string formula = "type your formula here"; //or get it from DB
StringToFormula stf = new StringToFormula();
double result = stf.Eval(formula);

1
投票

公式评估的方法有很多, 看一下。 只需输入您的输入,将字符串中的

a
b
n
字符替换为用户提供的值,并使用提到的方法之一解析方程式。


1
投票

我认为这就是解决方案

 Expression e = new Expression("((a+b+c)^n+b)");
 e.Evaluate();

0
投票
string input= "(12 + 4 * 6) * ((2 + 3 * ( 4 + 2 ) ) ( 5 + 12 ))";       
    string str4 = "(" + input`enter code here`.Replace(" ", "") + ")";
            str4 = str4.Replace(")(", ")*(");
            while (str4.Contains('('))
            {
                string sub1 = str4.Substring(str4.LastIndexOf("(") + 1);
                string sub = sub1.Substring(0, sub1.IndexOf(")"));
                string sub2 = sub;
                string str21 = sub2.Replace("^", "~^~").Replace("/", "~/~").Replace("*", "~*~").Replace("+", "~+~").Replace("-", "~-~");
                List<string> str31 = str21.Split('~').ToList();
                while (str31.Count > 1)
                {
                    while (str31.Contains("*"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "*")
                            {
                                val = Convert.ToDouble(str31[i - 1]) * Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                    while (str31.Contains("/"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "/")
                            {
                                val = Convert.ToDouble(str31[i - 1]) / Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                    while (str31.Contains("+"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "+")
                            {
                                val = Convert.ToDouble(str31[i - 1]) + Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                    while (str31.Contains("-"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "-")
                            {
                                val = Convert.ToDouble(str31[i - 1]) - Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                }
                str4 = str4.Replace("(" + sub + ")", str31[0].ToString());
            }

            string sum = str4;

0
投票

我想到的最具结构性的方法是定义一个由运算符符号(在你的例子中显然是 +、-、*、/ 和 ^)和操作数组成的语法;然后,如果定义的语法中存在输入的推导,则该推导基本上是表达式树,然后可以递归地遍历该表达式树,同时将运算符直接转换为实际操作。我承认描述有点模糊,但是良好的解析可能有点困难。也许看看 LL 解析器 会有所帮助。


0
投票

将字符串转换为数学表达式

var s3 = "3 - 4 + 5 * 9"

var s4 = NSExpression(format: s3).expressionValue(with: nil, context: nil) as! Double // 44.0

答案:44


0
投票

旧帖子,但我喜欢发布的解决方案源,并认为我会发布一个固定版本。它使用上面发布的源,但现在可以正确计算具有运算符优先级和负数的结果。例如。

10 + 12*10 - -47 % 30.0 = 147
-2 + -4 * (-2 - -2*10) = -74

public static class StringToFormula
{
    private static readonly string[] operators = { "+", "-", "/", "%", "*", "^" };
    private static readonly Func<double, double, double>[] operations = {
        (a1, a2) => a1 + a2,
        (a1, a2) => a1 - a2,
        (a1, a2) => a1 / a2,
        (a1, a2) => a1 % a2,
        (a1, a2) => a1 * a2,
        (a1, a2) => Math.Pow(a1, a2)
    };

    public static bool TryEval(string expression, out double value)
    {
        try
        {
            value = Eval(expression);
            return true;
        }
        catch
        {
            value = 0.0;
            return false;
        }
    }

    public static double Eval(string expression)
    {
        if (string.IsNullOrEmpty(expression))
            return 0.0;

        if (double.TryParse(expression, NumberStyles.Any, CultureInfo.InvariantCulture, out double value))
            return value;

        List<string> tokens = GetTokens(expression);
       tokens.Add("$"); // Append end of expression token
        Stack<double> operandStack = new Stack<double>();
        Stack<string> operatorStack = new Stack<string>();
        int tokenIndex = 0;

        while (tokenIndex < tokens.Count - 1)
        {
            string token = tokens[tokenIndex];
            string nextToken = tokens[tokenIndex + 1];
            
            switch (token)
            {
                case "(":
                {
                    string subExpr = GetSubExpression(tokens, ref tokenIndex);
                    operandStack.Push(Eval(subExpr));
                    continue;
                }
                case ")":
                    throw new ArgumentException("Mis-matched parentheses in expression");
                
                // Handle unary ops
                case "-":
                case "+":
                {
                    if (!IsOperator(nextToken) && operatorStack.Count == operandStack.Count)
                    {
                        operandStack.Push(double.Parse($"{token}{nextToken}", CultureInfo.InvariantCulture));
                        tokenIndex += 2;
                        continue;
                    }
                }
                break;
            }
            
            if (IsOperator(token))
            {
                while (operatorStack.Count > 0 && OperatorPrecedence(token) <= OperatorPrecedence(operatorStack.Peek()))
                {
                    if (!ResolveOperation()) {
                        throw new ArgumentException(BuildOpError());
                    }
                }
                operatorStack.Push(token);
            }
            else
            {
                operandStack.Push(double.Parse(token, CultureInfo.InvariantCulture));
            }
            tokenIndex += 1;
        }

        while (operatorStack.Count > 0)
        {
            if (!ResolveOperation())
                throw new ArgumentException(BuildOpError());
        }

        return operandStack.Pop();

        bool IsOperator(string token)
        {
            return Array.IndexOf(operators, token) >= 0;
        }
        int OperatorPrecedence(string op)
        {
            switch (op)
            {
            case "^":
                return 3;
            case "*":
            case "/":
            case "%":
                return 2;
                
            case "+":
            case "-":
                return 1;
            default:
                return 0;
            }
        }
        
        string BuildOpError() {
            string op = operatorStack.Pop();
            string rhs = operandStack.Any() ? operandStack.Pop().ToString() : "null";
            string lhs = operandStack.Any() ? operandStack.Pop().ToString() : "null";
            return $"Operation not supported: {lhs} {op} {rhs}";
        }
       
        bool ResolveOperation()
        {
            if (operandStack.Count < 2)
            {
                return false;
            }
            
            string op = operatorStack.Pop();
            double rhs = operandStack.Pop();
            double lhs = operandStack.Pop();
            operandStack.Push(operations[Array.IndexOf(operators, op)](lhs, rhs));
            Console.WriteLine($"Resolve {lhs} {op} {rhs} = {operandStack.Peek()}");
            return true;
        }
    }

    private static string GetSubExpression(List<string> tokens, ref int index)
    {
        StringBuilder subExpr = new StringBuilder();
        int parenlevels = 1;
        index += 1;
        while (index < tokens.Count && parenlevels > 0)
        {
            string token = tokens[index];
            switch (token) {
                case "(": parenlevels += 1; break;
                case ")": parenlevels -= 1; break;
            }
            
            if (parenlevels > 0)
                subExpr.Append(token);
            
            index += 1;
        }

        if (parenlevels > 0)
            throw new ArgumentException("Mis-matched parentheses in expression");
        
        return subExpr.ToString();
    }

    private static List<string> GetTokens(string expression)
    {
        string operators = "()^*/%+-";
        List<string> tokens = new List<string>();
        StringBuilder sb = new StringBuilder();

        foreach (char c in expression.Replace(" ", string.Empty))
        {
            if (operators.IndexOf(c) >= 0)
            {
                if ((sb.Length > 0))
                {
                    tokens.Add(sb.ToString());
                    sb.Length = 0;
                }
                tokens.Add(c.ToString());
            }
            else
            {
                sb.Append(c);
            }
        }

        if ((sb.Length > 0))
        {
            tokens.Add(sb.ToString());
        }
        return tokens;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.