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往上了
但是这样真的很明了啊,而且可以随时添加新的算法,简直了

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

posted @ 2018-03-09 17:11  爱喝可乐  阅读(1780)  评论(2编辑  收藏  举报