• C# 计算一串字符串算法


    工作中遇到一个小问题,就是要做一个类似excel那种的公式的东西,就是A0+A1*B0那样的公式,然后得出结果。

    首先,分析。

    这不是计算器,计算器要比这个简易,计算器是所按即所得,即你点击+-之类的按钮时候,你的数字已经确认了,你所要做的只是转换一下string和decimal而已。

    比如1+2*(2+4)/4-1

    如果再算上幂运算,我打不出来幂运算的符号,尴尬。

    我们可以这么写,比如,遇到的第一个数字是1,那么定义一个变量firnum=1  第一个符号是+,定义一个变量 mark=+,第二个数字是2,顶一个一个变量secnum=2,第二个符号是*,这时候进行判断,因为*/比加减的运算级别高,要先算乘除,所以,这里1和+要缓存起来,继续往下走,然后计算(),得出()内的数字是6,这时候先运算2*6,然后遇到/,计算12/4,再往后走,遇到-,这时候+-的运算级别一样,则开始运算之前的1和+,然后依次运算,最后得出结果。

    怎么说呢,我们姑且认为这是一个方法吧,姑且认为,这么辛苦了,写了这么多代码,能进行四则运算,还挺正确,也不容易,没有功劳也有苦劳。

            public decimal CalcRet(string str)
            {
                //第一个数字
                string firStr = string.Empty;
                decimal firNum = 0m;
    
                //第二个数字;
                string secStr = string.Empty;
                decimal secNum = 0m;
    
                //temp数字
                string tempStr = string.Empty;
    
                //当前计算符号
                char curMark = '!';
                //结果
                decimal result = 0m;
    
                //上一个符号
                char lastMark = '!';
    
                for (int i = 0; i < str.Length; i++)
                {
                    char c = str[i];
    
                    //判断如果是数字和.
                    if ((47 < c && c < 58) || c == '.')
                    {
                        //除却第一次是第一个数字需要转换,以后都是第一个和第二个进行累加
                        if (curMark == '!')
                        {
                            firStr += c;
                        }
                        else
                        {
                            if (curMark == '+' || curMark == '-')
                            {
                                secStr += c;
                            }
                            else if (curMark == '*' || curMark == '/')
                            {
                                if (lastMark == '+' || lastMark == '-')
                                {
                                    tempStr += c;
                                }
                                else
                                {
                                    secStr += c;
                                }
                            }
                        }
    
                        continue;
                    }
    
                    if (firStr != "")
                    {
                        decimal.TryParse(firStr, out firNum);
                        firStr = "";
                    }
    
                    if (c == '+' || c == '-' || c == '*' || c == '/')
                    {
                        switch (curMark)
                        {
                            case '+':
                                if (secStr != "" && tempStr != "")
                                {
                                    secNum = OperCalc(curMark, secNum, tempStr);
                                    firNum = firNum + secNum;
                                    secStr = "";
                                    tempStr = "";
                                }
    
                                if (c == '*' || c == '/')
                                {
                                    lastMark = curMark;
                                    curMark = c;
                                    break;
                                }
    
    
                                if (secStr == "") continue;
    
                                firNum = OperCalc(curMark, firNum, secStr);
                                curMark = c;
                                lastMark = c;
                                secStr = "";
                                break;
                            case '-':
                                if (secStr != "" && tempStr != "")
                                {
                                    secNum = OperCalc(curMark, secNum, tempStr);
                                    firNum = firNum - secNum;
                                    secStr = "";
                                    tempStr = "";
                                }
    
                                if (c == '*' || c == '/')
                                {
                                    lastMark = curMark;
                                    curMark = c;
                                    break;
                                }
    
    
                                if (secStr == "") continue;
    
                                firNum = OperCalc(curMark, firNum, secStr);
                                curMark = c;
                                lastMark = c;
                                secStr = "";
                                break;
                            case '*':
    
                                if (lastMark != '!' && tempStr != "")
                                {
                                    secNum = OperCalc(curMark, secStr, tempStr);
                                    secStr = secNum.ToString();
                                    tempStr = "";
    
                                }
                                else
                                {
                                    firNum = OperCalc(curMark, firNum, secStr);
                                    secStr = "";
                                    curMark = c;
                                    break;
                                }
    
                                if (c == '+' || c == '-')
                                {
                                    if (lastMark != '!')
                                    {
                                        firNum = OperCalc(lastMark, firNum, secNum);
                                        secStr = "";
                                        tempStr = "";
                                    }
                                }
    
                                curMark = c;
                                break;
                            case '/':
    
                                if (lastMark != '!' && tempStr != "")
                                {
                                    secNum = OperCalc(curMark, secStr, tempStr);
                                    secStr = secNum.ToString();
                                    tempStr = "";
                                }
                                else
                                {
                                    firNum = OperCalc(curMark, firNum, secStr);
                                    secStr = "";
                                    curMark = c;
                                    break;
                                }
    
                                if (c == '+' || c == '-')
                                {
                                    if (lastMark != '!')
                                    {
                                        firNum = OperCalc(lastMark, firNum, secNum);
                                        secStr = "";
                                        tempStr = "";
                                    }
                                }
    
                                curMark = c;
                                break;
                            case '(':
                                break;
                            case ')':
                                break;
                            default:
                                curMark = c;
                                if (c == '+' || c == '-')
                                    lastMark = c;
                                break;
                        }
                    }
                    else if (c == '(')
                    {
                        int temp = 1;
                        for (int j = i + 1; j < str.Length; j++)
                        {
                            var k = str[j];
                            if (k == '(')
                            {
                                temp++;
                            }
                            else if (k == ')')
                            {
                                temp--;
                            }
    
                            if (temp == 0)
                            {
                                temp = j - i - 1;
                            }
                        }
    
                        var kh = CalcRet(str.Substring(i + 1, temp));
                        if (lastMark != '!')
                        {
    
                            if (secStr != "")
                            {
                                tempStr = kh.ToString();
                            }
                            else
                            {
                                secNum = kh;
                                secStr = kh.ToString();
                            }
                        }
                        else
                        {
                            if (i == 0)
                            {
                                firNum = kh;
                            }
                            else
                            {
                                secNum = kh;
                                secStr = kh.ToString();
                            }
                        }
    
                        i += temp + 1;
                    }
    
    
                }
                if (tempStr != "")
                {
                    secNum = OperCalc(curMark, secStr, tempStr);
                    secStr = secNum.ToString();
                    result = OperCalc(lastMark, firNum, secStr);
                }
                else
                {
                    result = OperCalc(curMark, firNum, secStr);
                }
                return result;
    
            }
    
            decimal OperCalc(char mark, string fir, string sec)
            {
                decimal a, b;
                decimal.TryParse(fir, out a);
                decimal.TryParse(sec, out b);
                switch (mark)
                {
                    case '+':
                        return a + b;
                    case '-':
                        return a - b;
                    case '*':
                        return a * b;
                    case '/':
                        return a / b;
                    default:
                        return 0m;
                }
            }
    
            decimal OperCalc(char mark, decimal fir, string sec)
            {
                decimal b;
                decimal.TryParse(sec, out b);
                switch (mark)
                {
                    case '+':
                        return fir + b;
                    case '-':
                        return fir - b;
                    case '*':
                        return fir * b;
                    case '/':
                        return fir / b;
                    default:
                        return 0m;
                }
            }
    
            decimal OperCalc(char mark, decimal fir, decimal sec)
            {
                switch (mark)
                {
                    case '+':
                        return fir + sec;
                    case '-':
                        return fir - sec;
                    case '*':
                        return fir * sec;
                    case '/':
                        return fir / sec;
                    default:
                        return 0m;
                }
            }

    对,就是这种写法。

    在我看来,这么写的代码,真的只是一堆垃圾,我不是针对谁,我是说写成这样的逻辑,它就是垃圾,连没毕业的大学生都不如。

    比如,如果加幂运算如果加mod怎么办,我就问你怎么办?

    继续判断?

    写不死你!

    然后,我们可以换个思路。

    所谓运算,不过是两个数字和一个符号之间故事,抱歉,我觉得一对一那种男女关系不适用于这里,开个玩笑,呵呵!强行尬聊~

    1+2  是1,2 两个数字和+之间的运算

    1+2+3*(4+5),是12345数字和四个符号进行的运算,至于括号,我们是不是可以把括号当成一个递归?就是4+5当成一个递归,调用同一个函数,返回一个结果就行了

    也就是说,数字永远比符号多一个

    我们是不是可以这么想。

    list1 ={1,2,3,4,5}

    list2={+,+,*,(+)}

    第一次运算后

    list1 ={1,2,3,9}

    list2={+,+,*}

    按照优先级,我们可先计算*

    得到

    list1 ={1,2,3,9}{1,2,27}

    list2={+,+,*}{+,+}

    删掉*和最后的9,同时删掉一个符号和一个数字,得到

    list1 ={1,2,27}

    list2={+,+}

    继续

    list1 ={3,27}

    list2={+}

    再继续

    list1 ={30}

    list2={}

    最后就剩下一个数字,好,计算完毕

    public class CalcOperation
        {
            /// <summary>
            /// 计算字符串解析表达式 1+2(2*(3+4))
            /// </summary>
            /// <param name="str">传入的字符串</param>
            /// <returns>计算得到的结果</returns>
            public decimal CalcStr(string str)
            {
                decimal num = 0m;
                //数字集合
                List<decimal> numList = new List<decimal>();
                //操作符集合
                List<Operation> operList = new List<Operation>();
                string strNum = "";
                for (int i = 0; i < str.Length; i++)
                {
                    char c = str[i];
    
                    //判断如果是数字和.
                    if ((47 < c && c < 58) || c == '.')
                    {
                        strNum += c;
    
                        if (i == str.Length - 1)
                        {
                            if (!string.IsNullOrEmpty(strNum))
                            {
                                decimal.TryParse(strNum, out num);
                                numList.Add(num);
                                strNum = "";
                            }
                        }
                        continue;
                    }
                    else if (c == '(')
                    {
                        int temp = 1;
                        for (int j = i + 1; j < str.Length; j++)
                        {
                            var k = str[j];
                            if (k == '(')
                            {
                                temp++;
                            }
                            else if (k == ')')
                            {
                                temp--;
                            }
    
                            if (temp == 0)
                            {
                                temp = j - i - 1;
                            }
                        }
    
                        strNum = str.Substring(i + 1, temp);
                        numList.Add(CalcStr(strNum));
                        strNum = "";
                        i += temp + 1;
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(strNum))
                        {
                            decimal.TryParse(strNum, out num);
                            numList.Add(num);
                            strNum = "";
                        }
    
                        if (c == '+')
                        {
                            operList.Add(new AddOperation());
                        }
                        else if (c == '-')
                        {
                            operList.Add(new SubOperation());
                        }
                        else if (c == '*')
                        {
                            operList.Add(new MultipOperation());
                        }
                        else if (c == '/')
                        {
                            operList.Add(new DivOperation());
                        }
                        else if (c == '%')
                        {
                            operList.Add(new ModOperation());
                        }
                        else
                        {
                            operList.Add(null);
                        }
                    }
                }
    
                List<int> tempOrder = new List<int>();
                operList.ForEach(w =>
                {
                    if (!tempOrder.Contains(w.PrioRity))
                    {
                        tempOrder.Add(w.PrioRity);
                    }
    
                });
    
                tempOrder.Sort();
                for (int t = 0; t < tempOrder.Count; t++)
                {
                    for (int i = 0; i < operList.Count; i++)
                    {
                        if (operList[i].PrioRity == tempOrder[t])
                        {
                            numList[i] = operList[i].OperationResult(numList[i], numList[i + 1]);
                            numList.RemoveAt(i + 1);
                            operList.RemoveAt(i);
                            i--;
                        }
                    }
                }
    
                if (numList.Count == 1) return numList[0];
    
                return 0m;
            }
    
            public class Operation
            {
                protected int priority = 99;
                /// <summary>
                /// 优先级
                /// </summary>
                public virtual int PrioRity
                {
                    get
                    {
                        return priority;
                    }
                    set
                    {
                        priority = value;
                    }
                }
    
                public virtual decimal OperationResult(decimal a, decimal b)
                {
                    return 0m;
                }
            }
    
            public class AddOperation : Operation
            {
                public override decimal OperationResult(decimal a, decimal b)
                {
                    return a + b;
                }
            }
    
            public class SubOperation : Operation
            {
                public override decimal OperationResult(decimal a, decimal b)
                {
                    return a - b;
                }
            }
    
            public class MultipOperation : Operation
            {
                public override int PrioRity
                {
                    get
                    {
                        return 98;
                    }
                }
    
                public override decimal OperationResult(decimal a, decimal b)
                {
                    return a * b;
                }
            }
    
            public class DivOperation : Operation
            {
                public override int PrioRity
                {
                    get
                    {
                        return 98;
                    }
                }
                public override decimal OperationResult(decimal a, decimal b)
                {
                    return a / b;
                }
            }
    
            public class ModOperation : Operation
            {
                public override int PrioRity
                {
                    get
                    {
                        return 97;
                    }
                }
                public override decimal OperationResult(decimal a, decimal b)
                {
                    return a % b;
                }
            }
    
        }
    PrioRity这个是优先级,我比较懒,就从99往上了
    但是这样真的很明了啊,而且可以随时添加新的算法,简直了

    我想说的是,能简便的尽量简便,能通运的尽量通用,自己看的舒服,别人看的也舒服,是不是~

  • 相关阅读:
    33. 搜索旋转排序数组
    54. 螺旋矩阵
    46. 全排列
    120. 三角形最小路径和
    338. 比特位计数
    746. 使用最小花费爬楼梯
    spring boot的一些常用注解
    SSM整合Dubbo案例
    一些面试题
    Spring Aop和Spring Ioc(二)
  • 原文地址:https://www.cnblogs.com/fish124423/p/8534939.html
Copyright © 2020-2023  润新知